diff --git a/contracts/.env.example b/contracts/.env.example index 243b768b0..66f5e7427 100644 --- a/contracts/.env.example +++ b/contracts/.env.example @@ -1,13 +1,26 @@ PRIVATE_KEY=0xabc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc1 -REPORT_GAS=true + INFURA_API_KEY=ABC123ABC123ABC123ABC123ABC123ABC1 +# Testing +REPORT_GAS=true + # There seems to be a bug with hardhat-deploy's implementation of etherscan-verify # If ETHERSCAN_API_KEY is set, it overrides any hardhat configuration. ETHERSCAN_API_KEY_FIX=ABC123ABC123ABC123ABC123ABC123ABC1 ARBISCAN_API_KEY=ABC123ABC123ABC123ABC123ABC123ABC1 GNOSISSCAN_API_KEY=ABC123ABC123ABC123ABC123ABC123ABC1 +# For the bots +LOG_LEVEL=debug +SUBGRAPH_URL=https://api.studio.thegraph.com/query/61738/kleros-v2-core-devnet/version/latest +LOGTAIL_TOKEN_KEEPER_BOT=cqPqBofVC8nmA8EZdZGqqvUV +LOGTAIL_TOKEN_RELAYER_BOT=41h8q5Z4gnz5yDx215eqcQ4r +LOGTAIL_TOKEN_DISPUTOR_BOT=mSryyvYubviaMqKDWfBKAGsi +HEARTBEAT_URL_KEEPER_BOT=https://uptime.betterstack.com/api/v1/heartbeat/jc23S8ZZzpf8KbzwxL1hoBp9 +HEARTBEAT_URL_RELAYER_BOT=https://uptime.betterstack.com/api/v1/heartbeat/eT6Trk6CddJV6fFBbqZNzyqC +DISPUTES_TO_SKIP= + # Optionally for debugging # TENDERLY_USERNAME=your_username # TENDERLY_PROJECT=your_project diff --git a/contracts/.npmignore b/contracts/.npmignore index 382ffca6e..ac1207574 100644 --- a/contracts/.npmignore +++ b/contracts/.npmignore @@ -1,2 +1,7 @@ # NOP, just force npm to disregard .gitignore # https://docs.npmjs.com/cli/v9/using-npm/developers#keeping-files-out-of-your-package + +.env* +.flaskenv* +!.env.project +!.env.vault \ No newline at end of file diff --git a/contracts/README.md b/contracts/README.md index 72c3c4a5a..cbafd3d02 100644 --- a/contracts/README.md +++ b/contracts/README.md @@ -4,7 +4,7 @@ Smart contracts for Kleros v2 ## Deployments -Refresh the list of deployed contracts by running `./scripts/generateDeploymentsMarkdown.sh`. +Refresh the list of deployed contracts by running `./scripts/generateDeploymentsMarkdown.sh` or `./scripts/populateReadme.sh`. ### Official Testnet @@ -57,7 +57,7 @@ Refresh the list of deployed contracts by running `./scripts/generateDeployments - [DisputeKitClassic: proxy](https://sepolia.arbiscan.io/address/0x9426F127116C3652A262AE1eA48391AC8F44D35b), [implementation](https://sepolia.arbiscan.io/address/0x692CC78F2570181FFB99297965FeAA8352ab12E8) - [DisputeResolver](https://sepolia.arbiscan.io/address/0xB8B36CC43f852f9F0484f53Eb38CaBBA28a81bF6) - [DisputeTemplateRegistry: proxy](https://sepolia.arbiscan.io/address/0x596D3B09E684D62217682216e9b7a0De75933391), [implementation](https://sepolia.arbiscan.io/address/0xc53b813ed94AaEb6F5518D60bf6a8109954bE3f6) -- [Escrow](https://sepolia.arbiscan.io/address/0xdaf749DABE7be6C6894950AE69af35c20a00ABd9) +- [Escrow](https://sepolia.arbiscan.io/address/0x10f7A6f42Af606553883415bc8862643A6e63fdA) - [EvidenceModule: proxy](https://sepolia.arbiscan.io/address/0x57fd453FB0d16f8ca174E7386102D7170E17Be09), [implementation](https://sepolia.arbiscan.io/address/0x05AD81f245209b7f91885fd96e57c9da90554824) - [KlerosCore: proxy](https://sepolia.arbiscan.io/address/0xA54e7A16d7460e38a8F324eF46782FB520d58CE8), [implementation](https://sepolia.arbiscan.io/address/0x91a373BBdE0532F86410682F362e2Cf685e95085) - [PNKFaucet](https://sepolia.arbiscan.io/address/0x7EFE468003Ad6A858b5350CDE0A67bBED58739dD) @@ -214,6 +214,8 @@ yarn sourcify --network +``` + +#### 3. Deploy to Public Testnets + +```bash +# ArbitrumSepolia to Chiado +yarn deploy --network arbitrumSepolia --tags Arbitration +yarn deploy --network arbitrumSepolia --tags HomeArbitrable +yarn deploy --network chiado --tags ForeignGatewayOnGnosis +yarn deploy --network chiado --tags KlerosLiquidOnGnosis +yarn deploy --network chiado --tags ForeignArbitrable +yarn deploy --network arbitrumSepolia --tags HomeGatewayToGnosis + +# Sepolia +yarn deploy --network sepolia --tags ForeignGatewayOnEthereum +yarn deploy --network sepolia --tags ForeignArbitrable +yarn deploy --network arbitrumSepolia --tags HomeGatewayToEthereum +``` + +The deployed addresses should be displayed to the screen after the deployment is complete. If you missed them, you can always go to the `deployments/` directory and look for the respective file. + +#### 4. Deploy a Devnet on Public Testnets + +Same steps as above but append `Devnet` to the `--network` parameter. + +#### Running Test Fixtures + +**Shell 1: the node** + +```bash +yarn hardhat node --tags Arbitration,VeaMock +``` + +**Shell 2: the test scripts** + +```bash +yarn test --network localhost +``` + +#### 4. Verify the Source Code + +This must be done for each network separately. + +```bash +# explorer +yarn etherscan-verify --network +yarn etherscan-verify-proxies + +# sourcify +yarn sourcify --network + +``` + +## Ad-hoc procedures + +### Populating the policy registry and courts + +The policy registry and courts configuration can be found in `config/policies.*.json` and `config/courts.*.json`. + +#### 1/ Export the registry data from V1 + +```bash +for network in mainnet gnosischain +do + yarn hardhat run scripts/getPoliciesV1.ts --network $network | tee config/policies.v1.$network.json + yarn hardhat run scripts/getCourtsV1.ts --network $network | tee config/courts.v1.$network.json +done +``` + +#### 2/ Import the data to V2 - Local Network + +Shell 1: + +```bash +yarn hardhat node --tags Arbitration +``` + +Shell 2: + +```bash +yarn hardhat run scripts/populateCourts.ts --network localhost +yarn hardhat run scripts/populatePolicyRegistry.ts --network localhost +``` + +#### 3/ Import the data to V2 - Public Testnet + +```bash +yarn hardhat run scripts/populateCourts.ts --network arbitrumSepolia +yarn hardhat run scripts/populatePolicyRegistry.ts --network arbitrumSepolia +``` + +### Generate deployment artifacts for existing contracts + +#### Usage + +```bash +scripts/generateDeploymentArtifact.sh
+``` + +#### Example: WETH on Gnosis chain + +```bash +scripts/generateDeploymentArtifact.sh gnosischain 0xf8d1677c8a0c961938bf2f9adc3f3cfda759a9d9 > deployments/gnosischain/WETH.json +``` + +### Push the contracts to a Tenderly project + +Ensure that your `$TENDERLY_PROJECT` and `$TENDERLY_USERNAME` is set correctly in `.env`. + +```bash +yarn tenderly-verify --network sepolia +yarn tenderly-verify --network arbitrumSepolia +``` diff --git a/contracts/config/courts.v1.gnosischain.json b/contracts/config/courts.v1.gnosischain.json index 66b25fb7f..f18c2de85 100644 --- a/contracts/config/courts.v1.gnosischain.json +++ b/contracts/config/courts.v1.gnosischain.json @@ -1,7 +1,7 @@ [ { "id": 0, - "parent": "0", + "parent": 0, "hiddenVotes": true, "minStake": "1600000000000000000000", "alpha": "10000", @@ -16,7 +16,7 @@ }, { "id": 1, - "parent": "0", + "parent": 0, "hiddenVotes": false, "minStake": "1600000000000000000000", "alpha": "5000", @@ -31,7 +31,7 @@ }, { "id": 2, - "parent": "0", + "parent": 0, "hiddenVotes": false, "minStake": "5800000000000000000000", "alpha": "5000", @@ -46,7 +46,7 @@ }, { "id": 3, - "parent": "2", + "parent": 2, "hiddenVotes": false, "minStake": "5800000000000000000000", "alpha": "1800", @@ -61,7 +61,7 @@ }, { "id": 4, - "parent": "2", + "parent": 2, "hiddenVotes": false, "minStake": "5800000000000000000000", "alpha": "1800", @@ -76,7 +76,7 @@ }, { "id": 5, - "parent": "2", + "parent": 2, "hiddenVotes": false, "minStake": "5800000000000000000000", "alpha": "1800", @@ -91,7 +91,7 @@ }, { "id": 6, - "parent": "2", + "parent": 2, "hiddenVotes": false, "minStake": "5800000000000000000000", "alpha": "2200", @@ -106,7 +106,7 @@ }, { "id": 7, - "parent": "2", + "parent": 2, "hiddenVotes": false, "minStake": "5800000000000000000000", "alpha": "2200", @@ -121,7 +121,7 @@ }, { "id": 8, - "parent": "2", + "parent": 2, "hiddenVotes": false, "minStake": "5800000000000000000000", "alpha": "2800", @@ -136,7 +136,7 @@ }, { "id": 9, - "parent": "2", + "parent": 2, "hiddenVotes": false, "minStake": "5800000000000000000000", "alpha": "2800", @@ -151,7 +151,7 @@ }, { "id": 10, - "parent": "2", + "parent": 2, "hiddenVotes": false, "minStake": "5800000000000000000000", "alpha": "2800", @@ -166,7 +166,7 @@ }, { "id": 11, - "parent": "2", + "parent": 2, "hiddenVotes": false, "minStake": "5800000000000000000000", "alpha": "2800", @@ -181,7 +181,7 @@ }, { "id": 12, - "parent": "0", + "parent": 0, "hiddenVotes": false, "minStake": "7400000000000000000000", "alpha": "5000", @@ -196,7 +196,7 @@ }, { "id": 13, - "parent": "12", + "parent": 12, "hiddenVotes": false, "minStake": "7400000000000000000000", "alpha": "5000", @@ -211,7 +211,7 @@ }, { "id": 14, - "parent": "12", + "parent": 12, "hiddenVotes": false, "minStake": "7400000000000000000000", "alpha": "5000", @@ -226,7 +226,7 @@ }, { "id": 15, - "parent": "0", + "parent": 0, "hiddenVotes": true, "minStake": "3300000000000000000000", "alpha": "5000", @@ -241,7 +241,7 @@ }, { "id": 16, - "parent": "15", + "parent": 15, "hiddenVotes": false, "minStake": "3300000000000000000000", "alpha": "2400", @@ -256,7 +256,7 @@ }, { "id": 17, - "parent": "15", + "parent": 15, "hiddenVotes": false, "minStake": "3300000000000000000000", "alpha": "4250", diff --git a/contracts/config/courts.v1.mainnet.json b/contracts/config/courts.v1.mainnet.json index 03fc9d8c7..6d5559ba6 100644 --- a/contracts/config/courts.v1.mainnet.json +++ b/contracts/config/courts.v1.mainnet.json @@ -1,7 +1,7 @@ [ { "id": 0, - "parent": "0", + "parent": 0, "hiddenVotes": false, "minStake": "1700000000000000000000", "alpha": "10000", @@ -16,7 +16,7 @@ }, { "id": 1, - "parent": "0", + "parent": 0, "hiddenVotes": false, "minStake": "5000000000000000000000", "alpha": "5000", @@ -31,7 +31,7 @@ }, { "id": 2, - "parent": "1", + "parent": 1, "hiddenVotes": false, "minStake": "5000000000000000000000", "alpha": "3900", @@ -46,7 +46,7 @@ }, { "id": 3, - "parent": "2", + "parent": 2, "hiddenVotes": false, "minStake": "10000000000000000000000", "alpha": "5000", @@ -61,7 +61,7 @@ }, { "id": 4, - "parent": "1", + "parent": 1, "hiddenVotes": false, "minStake": "23000000000000000000000", "alpha": "5000", @@ -76,7 +76,7 @@ }, { "id": 5, - "parent": "0", + "parent": 0, "hiddenVotes": false, "minStake": "14000000000000000000000", "alpha": "3250", @@ -91,7 +91,7 @@ }, { "id": 6, - "parent": "0", + "parent": 0, "hiddenVotes": false, "minStake": "3900000000000000000000", "alpha": "5000", @@ -106,7 +106,7 @@ }, { "id": 7, - "parent": "0", + "parent": 0, "hiddenVotes": false, "minStake": "14000000000000000000000", "alpha": "3250", @@ -121,7 +121,7 @@ }, { "id": 8, - "parent": "0", + "parent": 0, "hiddenVotes": false, "minStake": "1700000000000000000000", "alpha": "4000", @@ -136,7 +136,7 @@ }, { "id": 9, - "parent": "0", + "parent": 0, "hiddenVotes": false, "minStake": "4600000000000000000000", "alpha": "5000", @@ -151,7 +151,7 @@ }, { "id": 10, - "parent": "0", + "parent": 0, "hiddenVotes": false, "minStake": "3800000000000000000000", "alpha": "5000", @@ -166,7 +166,7 @@ }, { "id": 11, - "parent": "10", + "parent": 10, "hiddenVotes": false, "minStake": "4700000000000000000000", "alpha": "5000", @@ -181,7 +181,7 @@ }, { "id": 12, - "parent": "9", + "parent": 9, "hiddenVotes": false, "minStake": "4600000000000000000000", "alpha": "4100", @@ -196,7 +196,7 @@ }, { "id": 13, - "parent": "6", + "parent": 6, "hiddenVotes": false, "minStake": "3900000000000000000000", "alpha": "4000", @@ -211,7 +211,7 @@ }, { "id": 14, - "parent": "6", + "parent": 6, "hiddenVotes": false, "minStake": "3900000000000000000000", "alpha": "4000", @@ -226,7 +226,7 @@ }, { "id": 15, - "parent": "6", + "parent": 6, "hiddenVotes": false, "minStake": "3900000000000000000000", "alpha": "4000", @@ -241,7 +241,7 @@ }, { "id": 16, - "parent": "6", + "parent": 6, "hiddenVotes": false, "minStake": "3900000000000000000000", "alpha": "4000", @@ -256,7 +256,7 @@ }, { "id": 17, - "parent": "6", + "parent": 6, "hiddenVotes": false, "minStake": "3900000000000000000000", "alpha": "4000", @@ -271,7 +271,7 @@ }, { "id": 18, - "parent": "6", + "parent": 6, "hiddenVotes": false, "minStake": "3900000000000000000000", "alpha": "4300", @@ -286,7 +286,7 @@ }, { "id": 19, - "parent": "6", + "parent": 6, "hiddenVotes": false, "minStake": "3900000000000000000000", "alpha": "4300", @@ -301,7 +301,7 @@ }, { "id": 20, - "parent": "6", + "parent": 6, "hiddenVotes": false, "minStake": "3900000000000000000000", "alpha": "4300", @@ -316,7 +316,7 @@ }, { "id": 21, - "parent": "6", + "parent": 6, "hiddenVotes": false, "minStake": "3900000000000000000000", "alpha": "4300", @@ -331,7 +331,7 @@ }, { "id": 22, - "parent": "0", + "parent": 0, "hiddenVotes": true, "minStake": "2300000000000000000000", "alpha": "10000", @@ -346,7 +346,7 @@ }, { "id": 23, - "parent": "0", + "parent": 0, "hiddenVotes": false, "minStake": "16000000000000000000000", "alpha": "5000", diff --git a/contracts/config/courts.v2.devnet.json b/contracts/config/courts.v2.devnet.json index c7ffe9f1b..c1fdd831c 100644 --- a/contracts/config/courts.v2.devnet.json +++ b/contracts/config/courts.v2.devnet.json @@ -1,7 +1,7 @@ [ { "id": 1, - "parent": "0", + "parent": 0, "hiddenVotes": true, "minStake": "520000000000000000000", "alpha": "5000", @@ -16,7 +16,7 @@ }, { "id": 2, - "parent": "1", + "parent": 1, "hiddenVotes": false, "minStake": "520000000000000000000", "alpha": "3100", @@ -31,7 +31,7 @@ }, { "id": 3, - "parent": "1", + "parent": 1, "hiddenVotes": false, "minStake": "1200000000000000000000", "alpha": "5000", diff --git a/contracts/config/courts.v2.testnet.json b/contracts/config/courts.v2.testnet.json index 4f036d083..b25c863c3 100644 --- a/contracts/config/courts.v2.testnet.json +++ b/contracts/config/courts.v2.testnet.json @@ -1,47 +1,47 @@ [ { "id": 1, - "parent": "0", + "parent": 0, "hiddenVotes": false, "minStake": "150000000000000000000", "alpha": "5000", "feeForJuror": "10000000000000", "jurorsForCourtJump": "511", "timesPerPeriod": [ - 28800, - 28800, - 28800, - 28800 + 43200, + 43200, + 43200, + 43200 ] }, { "id": 2, - "parent": "1", + "parent": 1, "hiddenVotes": false, "minStake": "200000000000000000000", "alpha": "3100", "feeForJuror": "10000000000000", "jurorsForCourtJump": "30", "timesPerPeriod": [ - 28800, - 28800, - 28800, - 28800 + 43200, + 43200, + 43200, + 43200 ] }, { "id": 3, - "parent": "1", + "parent": 1, "hiddenVotes": false, "minStake": "200000000000000000000", "alpha": "5000", "feeForJuror": "10000000000000", "jurorsForCourtJump": "63", "timesPerPeriod": [ - 28800, - 28800, - 28800, - 28800 + 43200, + 43200, + 43200, + 43200 ] } ] diff --git a/contracts/deployments/arbitrumSepoliaDevnet/Escrow.json b/contracts/deployments/arbitrumSepoliaDevnet/Escrow.json index 993b60f74..f69d38177 100644 --- a/contracts/deployments/arbitrumSepoliaDevnet/Escrow.json +++ b/contracts/deployments/arbitrumSepoliaDevnet/Escrow.json @@ -1,5 +1,5 @@ { - "address": "0xdaf749DABE7be6C6894950AE69af35c20a00ABd9", + "address": "0x10f7A6f42Af606553883415bc8862643A6e63fdA", "abi": [ { "inputs": [ @@ -227,6 +227,12 @@ "name": "_transactionID", "type": "uint256" }, + { + "indexed": false, + "internalType": "string", + "name": "_transactionUri", + "type": "string" + }, { "indexed": true, "internalType": "address", @@ -244,6 +250,18 @@ "internalType": "uint256", "name": "_amount", "type": "uint256" + }, + { + "indexed": false, + "internalType": "string", + "name": "_asset", + "type": "string" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "_deadline", + "type": "uint256" } ], "name": "TransactionCreated", @@ -371,6 +389,11 @@ "name": "_timeoutPayment", "type": "uint256" }, + { + "internalType": "string", + "name": "_transactionUri", + "type": "string" + }, { "internalType": "address payable", "name": "_seller", @@ -671,34 +694,34 @@ "type": "function" } ], - "transactionHash": "0x3fec232b2cbc99d57bf970770be414dd979379762d5af18424d49924604154b5", + "transactionHash": "0x6fbc2d3d2b14f04ddf916a4bbb9654f41af20e0a5a591641be2daa13729317bd", "receipt": { "to": null, "from": "0xf1C7c037891525E360C59f708739Ac09A7670c59", - "contractAddress": "0xdaf749DABE7be6C6894950AE69af35c20a00ABd9", + "contractAddress": "0x10f7A6f42Af606553883415bc8862643A6e63fdA", "transactionIndex": 1, - "gasUsed": "24294257", - "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000008000000000000000004000000000000000000000000000000000000000040000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000080000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000040000000002000000400000000000100000000000000000000000000000000000000", - "blockHash": "0x4a245ca111980fc1afe84e9bb51e8ac4b701f6b66982b27a5f5538e13638766d", - "transactionHash": "0x3fec232b2cbc99d57bf970770be414dd979379762d5af18424d49924604154b5", + "gasUsed": "7823720", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000008000000000004000004000000001000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000002000000400000000000100000000000000000000000000000000000000", + "blockHash": "0x0cf0d7540ad3c61fd99217cd0c09a61469efcc463298d8f857c3970a4fa8d755", + "transactionHash": "0x6fbc2d3d2b14f04ddf916a4bbb9654f41af20e0a5a591641be2daa13729317bd", "logs": [ { "transactionIndex": 1, - "blockNumber": 3639224, - "transactionHash": "0x3fec232b2cbc99d57bf970770be414dd979379762d5af18424d49924604154b5", + "blockNumber": 6159752, + "transactionHash": "0x6fbc2d3d2b14f04ddf916a4bbb9654f41af20e0a5a591641be2daa13729317bd", "address": "0x596D3B09E684D62217682216e9b7a0De75933391", "topics": [ "0x00f7cd7255d1073b4e136dd477c38ea0020c051ab17110cc5bfab0c840ff9924", - "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x0000000000000000000000000000000000000000000000000000000000000043", "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470" ], "data": "0x000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001c6469737075746554656d706c6174654d617070696e673a20544f444f00000000", "logIndex": 0, - "blockHash": "0x4a245ca111980fc1afe84e9bb51e8ac4b701f6b66982b27a5f5538e13638766d" + "blockHash": "0x0cf0d7540ad3c61fd99217cd0c09a61469efcc463298d8f857c3970a4fa8d755" } ], - "blockNumber": 3639224, - "cumulativeGasUsed": "24294257", + "blockNumber": 6159752, + "cumulativeGasUsed": "7823720", "status": 1, "byzantium": true }, @@ -732,11 +755,11 @@ "0x596D3B09E684D62217682216e9b7a0De75933391", 600 ], - "numDeployments": 1, - "solcInputHash": "4ee8a1f2013c130bec1668c5304bc76a", - "metadata": "{\"compiler\":{\"version\":\"0.8.18+commit.87f61d96\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[{\"internalType\":\"contract IArbitratorV2\",\"name\":\"_arbitrator\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"_arbitratorExtraData\",\"type\":\"bytes\"},{\"internalType\":\"string\",\"name\":\"_templateData\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"_templateDataMappings\",\"type\":\"string\"},{\"internalType\":\"contract IDisputeTemplateRegistry\",\"name\":\"_templateRegistry\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_feeTimeout\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"ArbitratorOnly\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"BuyerFeeNotCoverArbitrationCosts\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"BuyerOnly\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"DeadlineNotPassed\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"DisputeAlreadyCreatedOrTransactionAlreadyExecuted\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"DisputeAlreadyResolved\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"GovernorOnly\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidRuling\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"MaximumPaymentAmountExceeded\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NotWaitingForBuyerFees\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NotWaitingForSellerFees\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"SellerFeeNotCoverArbitrationCosts\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"SellerOnly\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"TimeoutNotPassed\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"TransactionDisputed\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"contract IArbitratorV2\",\"name\":\"_arbitrator\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"_arbitrableDisputeID\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_externalDisputeID\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_templateId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"_templateUri\",\"type\":\"string\"}],\"name\":\"DisputeRequest\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"_transactionID\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"enum Escrow.Party\",\"name\":\"_party\",\"type\":\"uint8\"}],\"name\":\"HasToPayFee\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"_transactionID\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"_party\",\"type\":\"address\"}],\"name\":\"Payment\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"contract IArbitratorV2\",\"name\":\"_arbitrator\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"_disputeID\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_ruling\",\"type\":\"uint256\"}],\"name\":\"Ruling\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"_transactionID\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"_buyer\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"_seller\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"}],\"name\":\"TransactionCreated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"_transactionID\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"enum Escrow.Resolution\",\"name\":\"_resolution\",\"type\":\"uint8\"}],\"name\":\"TransactionResolved\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"AMOUNT_OF_CHOICES\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"arbitrator\",\"outputs\":[{\"internalType\":\"contract IArbitratorV2\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"arbitratorExtraData\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IArbitratorV2\",\"name\":\"_arbitrator\",\"type\":\"address\"}],\"name\":\"changeArbitrator\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"_arbitratorExtraData\",\"type\":\"bytes\"}],\"name\":\"changeArbitratorExtraData\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"_templateData\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"_templateDataMappings\",\"type\":\"string\"}],\"name\":\"changeDisputeTemplate\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IDisputeTemplateRegistry\",\"name\":\"_templateRegistry\",\"type\":\"address\"}],\"name\":\"changeTemplateRegistry\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_timeoutPayment\",\"type\":\"uint256\"},{\"internalType\":\"address payable\",\"name\":\"_seller\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"_templateData\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"_templateDataMappings\",\"type\":\"string\"}],\"name\":\"createTransaction\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"transactionID\",\"type\":\"uint256\"}],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"disputeIDtoTransactionID\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_transactionID\",\"type\":\"uint256\"}],\"name\":\"executeTransaction\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"feeTimeout\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getCountTransactions\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"governor\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_transactionID\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"}],\"name\":\"pay\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_transactionID\",\"type\":\"uint256\"}],\"name\":\"payArbitrationFeeByBuyer\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_transactionID\",\"type\":\"uint256\"}],\"name\":\"payArbitrationFeeBySeller\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_transactionID\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_amountReimbursed\",\"type\":\"uint256\"}],\"name\":\"reimburse\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_disputeID\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_ruling\",\"type\":\"uint256\"}],\"name\":\"rule\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"templateId\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"templateRegistry\",\"outputs\":[{\"internalType\":\"contract IDisputeTemplateRegistry\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_transactionID\",\"type\":\"uint256\"}],\"name\":\"timeOutByBuyer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_transactionID\",\"type\":\"uint256\"}],\"name\":\"timeOutBySeller\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"transactions\",\"outputs\":[{\"internalType\":\"address payable\",\"name\":\"buyer\",\"type\":\"address\"},{\"internalType\":\"address payable\",\"name\":\"seller\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"deadline\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"disputeID\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"buyerFee\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"sellerFee\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"lastFeePaymentTime\",\"type\":\"uint256\"},{\"internalType\":\"string\",\"name\":\"templateData\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"templateDataMappings\",\"type\":\"string\"},{\"internalType\":\"enum Escrow.Status\",\"name\":\"status\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"}],\"devdoc\":{\"details\":\"MultipleArbitrableTransaction contract that is compatible with V2. Adapted from https://github.com/kleros/kleros-interaction/blob/master/contracts/standard/arbitration/MultipleArbitrableTransaction.sol\",\"events\":{\"DisputeRequest(address,uint256,uint256,uint256,string)\":{\"details\":\"To be emitted when a dispute is created to link the correct meta-evidence to the disputeID.\",\"params\":{\"_arbitrableDisputeID\":\"The identifier of the dispute in the Arbitrable contract.\",\"_arbitrator\":\"The arbitrator of the contract.\",\"_externalDisputeID\":\"An identifier created outside Kleros by the protocol requesting arbitration.\",\"_templateId\":\"The identifier of the dispute template. Should not be used with _templateUri.\",\"_templateUri\":\"The URI to the dispute template. For example on IPFS: starting with '/ipfs/'. Should not be used with _templateId.\"}},\"HasToPayFee(uint256,uint8)\":{\"details\":\"Indicate that a party has to pay a fee or would otherwise be considered as losing.\",\"params\":{\"_party\":\"The party who has to pay.\",\"_transactionID\":\"The index of the transaction.\"}},\"Payment(uint256,uint256,address)\":{\"details\":\"To be emitted when a party pays or reimburses the other.\",\"params\":{\"_amount\":\"The amount paid.\",\"_party\":\"The party that paid.\",\"_transactionID\":\"The index of the transaction.\"}},\"Ruling(address,uint256,uint256)\":{\"details\":\"To be raised when a ruling is given.\",\"params\":{\"_arbitrator\":\"The arbitrator giving the ruling.\",\"_disputeID\":\"The identifier of the dispute in the Arbitrator contract.\",\"_ruling\":\"The ruling which was given.\"}},\"TransactionCreated(uint256,address,address,uint256)\":{\"details\":\"Emitted when a transaction is created.\",\"params\":{\"_amount\":\"The initial amount in the transaction.\",\"_buyer\":\"The address of the buyer.\",\"_seller\":\"The address of the seller.\",\"_transactionID\":\"The index of the transaction.\"}},\"TransactionResolved(uint256,uint8)\":{\"details\":\"To be emitted when a transaction is resolved, either by its execution, a timeout or because a ruling was enforced.\",\"params\":{\"_resolution\":\"Short description of what caused the transaction to be solved.\",\"_transactionID\":\"The ID of the respective transaction.\"}}},\"kind\":\"dev\",\"methods\":{\"constructor\":{\"details\":\"Constructor.\",\"params\":{\"_arbitrator\":\"The arbitrator of the contract.\",\"_arbitratorExtraData\":\"Extra data for the arbitrator.\",\"_feeTimeout\":\"Arbitration fee timeout for the parties.\",\"_templateData\":\"The dispute template data.\",\"_templateDataMappings\":\"The dispute template data mappings.\",\"_templateRegistry\":\"The dispute template registry.\"}},\"createTransaction(uint256,address,string,string)\":{\"details\":\"Create a transaction.\",\"params\":{\"_seller\":\"The recipient of the transaction.\",\"_templateData\":\"The dispute template data.\",\"_templateDataMappings\":\"The dispute template data mappings.\",\"_timeoutPayment\":\"Time after which a party can automatically execute the arbitrable transaction.\"},\"returns\":{\"transactionID\":\"The index of the transaction.\"}},\"executeTransaction(uint256)\":{\"details\":\"Transfer the transaction's amount to the seller if the timeout has passed.\",\"params\":{\"_transactionID\":\"The index of the transaction.\"}},\"getCountTransactions()\":{\"details\":\"Getter to know the count of transactions.\",\"returns\":{\"_0\":\"The count of transactions.\"}},\"pay(uint256,uint256)\":{\"details\":\"Pay seller. To be called if the good or service is provided.\",\"params\":{\"_amount\":\"Amount to pay in wei.\",\"_transactionID\":\"The index of the transaction.\"}},\"payArbitrationFeeByBuyer(uint256)\":{\"details\":\"Pay the arbitration fee to raise a dispute. To be called by the buyer. Note that the arbitrator can have createDispute throw, which will make this function throw and therefore lead to a party being timed-out. This is not a vulnerability as the arbitrator can rule in favor of one party anyway.\",\"params\":{\"_transactionID\":\"The index of the transaction.\"}},\"payArbitrationFeeBySeller(uint256)\":{\"details\":\"Pay the arbitration fee to raise a dispute. To be called by the seller. Note that this function mirrors payArbitrationFeeByBuyer.\",\"params\":{\"_transactionID\":\"The index of the transaction.\"}},\"reimburse(uint256,uint256)\":{\"details\":\"Reimburse buyer. To be called if the good or service can't be fully provided.\",\"params\":{\"_amountReimbursed\":\"Amount to reimburse in wei.\",\"_transactionID\":\"The index of the transaction.\"}},\"rule(uint256,uint256)\":{\"details\":\"Give a ruling for a dispute. Must be called by the arbitrator to enforce the final ruling. The purpose of this function is to ensure that the address calling it has the right to rule on the contract.\",\"params\":{\"_disputeID\":\"ID of the dispute in the Arbitrator contract.\",\"_ruling\":\"Ruling given by the arbitrator. Note that 0 is reserved for \\\"Refuse to arbitrate\\\".\"}},\"timeOutByBuyer(uint256)\":{\"details\":\"Reimburse buyer if seller fails to pay the fee.\",\"params\":{\"_transactionID\":\"The index of the transaction.\"}},\"timeOutBySeller(uint256)\":{\"details\":\"Pay seller if buyer fails to pay the fee.\",\"params\":{\"_transactionID\":\"The index of the transaction.\"}}},\"title\":\"Escrow for a sale paid in ETH and no fees.\",\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"src/arbitration/arbitrables/Escrow.sol\":\"Escrow\"},\"evmVersion\":\"paris\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\",\"useLiteralContent\":true},\"optimizer\":{\"enabled\":true,\"runs\":100},\"remappings\":[]},\"sources\":{\"@openzeppelin/contracts/token/ERC20/IERC20.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev Interface of the ERC20 standard as defined in the EIP.\\n */\\ninterface IERC20 {\\n /**\\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\\n * another (`to`).\\n *\\n * Note that `value` may be zero.\\n */\\n event Transfer(address indexed from, address indexed to, uint256 value);\\n\\n /**\\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\\n * a call to {approve}. `value` is the new allowance.\\n */\\n event Approval(address indexed owner, address indexed spender, uint256 value);\\n\\n /**\\n * @dev Returns the amount of tokens in existence.\\n */\\n function totalSupply() external view returns (uint256);\\n\\n /**\\n * @dev Returns the amount of tokens owned by `account`.\\n */\\n function balanceOf(address account) external view returns (uint256);\\n\\n /**\\n * @dev Moves `amount` tokens from the caller's account to `to`.\\n *\\n * Returns a boolean value indicating whether the operation succeeded.\\n *\\n * Emits a {Transfer} event.\\n */\\n function transfer(address to, uint256 amount) external returns (bool);\\n\\n /**\\n * @dev Returns the remaining number of tokens that `spender` will be\\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\\n * zero by default.\\n *\\n * This value changes when {approve} or {transferFrom} are called.\\n */\\n function allowance(address owner, address spender) external view returns (uint256);\\n\\n /**\\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\\n *\\n * Returns a boolean value indicating whether the operation succeeded.\\n *\\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\\n * that someone may use both the old and the new allowance by unfortunate\\n * transaction ordering. One possible solution to mitigate this race\\n * condition is to first reduce the spender's allowance to 0 and set the\\n * desired value afterwards:\\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\\n *\\n * Emits an {Approval} event.\\n */\\n function approve(address spender, uint256 amount) external returns (bool);\\n\\n /**\\n * @dev Moves `amount` tokens from `from` to `to` using the\\n * allowance mechanism. `amount` is then deducted from the caller's\\n * allowance.\\n *\\n * Returns a boolean value indicating whether the operation succeeded.\\n *\\n * Emits a {Transfer} event.\\n */\\n function transferFrom(address from, address to, uint256 amount) external returns (bool);\\n}\\n\",\"keccak256\":\"0x287b55befed2961a7eabd7d7b1b2839cbca8a5b80ef8dcbb25ed3d4c2002c305\",\"license\":\"MIT\"},\"src/arbitration/arbitrables/Escrow.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n\\n/// @authors: [@unknownunknown1, @fnanni-0, @shalzz, @jaybuidl]\\n/// @reviewers: []\\n/// @auditors: []\\n/// @bounties: []\\n\\npragma solidity 0.8.18;\\n\\nimport {IArbitrableV2, IArbitratorV2} from \\\"../interfaces/IArbitrableV2.sol\\\";\\nimport \\\"../interfaces/IDisputeTemplateRegistry.sol\\\";\\n\\n/// @title Escrow for a sale paid in ETH and no fees.\\n/// @dev MultipleArbitrableTransaction contract that is compatible with V2.\\n/// Adapted from https://github.com/kleros/kleros-interaction/blob/master/contracts/standard/arbitration/MultipleArbitrableTransaction.sol\\ncontract Escrow is IArbitrableV2 {\\n // ************************************* //\\n // * Enums / Structs * //\\n // ************************************* //\\n\\n enum Party {\\n None,\\n Buyer, // Makes a purchase in ETH.\\n Seller // Provides a good or service in exchange for ETH.\\n }\\n\\n enum Status {\\n NoDispute,\\n WaitingBuyer,\\n WaitingSeller,\\n DisputeCreated,\\n TransactionResolved\\n }\\n\\n enum Resolution {\\n TransactionExecuted,\\n TimeoutByBuyer,\\n TimeoutBySeller,\\n RulingEnforced\\n }\\n\\n struct Transaction {\\n address payable buyer;\\n address payable seller;\\n uint256 amount;\\n uint256 deadline; // Timestamp at which the transaction can be automatically executed if not disputed.\\n uint256 disputeID; // If dispute exists, the ID of the dispute.\\n uint256 buyerFee; // Total fees paid by the buyer.\\n uint256 sellerFee; // Total fees paid by the seller.\\n uint256 lastFeePaymentTime; // Last time the dispute fees were paid by either party.\\n string templateData;\\n string templateDataMappings;\\n Status status;\\n }\\n\\n // ************************************* //\\n // * Storage * //\\n // ************************************* //\\n\\n uint256 public constant AMOUNT_OF_CHOICES = 2;\\n address public immutable governor;\\n IArbitratorV2 public arbitrator; // Address of the arbitrator contract.\\n bytes public arbitratorExtraData; // Extra data to set up the arbitration.\\n IDisputeTemplateRegistry public templateRegistry; // The dispute template registry.\\n uint256 public templateId; // The current dispute template identifier.\\n uint256 public immutable feeTimeout; // Time in seconds a party can take to pay arbitration fees before being considered unresponsive and lose the dispute.\\n Transaction[] public transactions; // List of all created transactions.\\n mapping(uint256 => uint256) public disputeIDtoTransactionID; // Naps dispute ID to tx ID.\\n\\n // ************************************* //\\n // * Events * //\\n // ************************************* //\\n\\n /// @dev To be emitted when a party pays or reimburses the other.\\n /// @param _transactionID The index of the transaction.\\n /// @param _amount The amount paid.\\n /// @param _party The party that paid.\\n event Payment(uint256 indexed _transactionID, uint256 _amount, address _party);\\n\\n /// @dev Indicate that a party has to pay a fee or would otherwise be considered as losing.\\n /// @param _transactionID The index of the transaction.\\n /// @param _party The party who has to pay.\\n event HasToPayFee(uint256 indexed _transactionID, Party _party);\\n\\n /// @dev Emitted when a transaction is created.\\n /// @param _transactionID The index of the transaction.\\n /// @param _buyer The address of the buyer.\\n /// @param _seller The address of the seller.\\n /// @param _amount The initial amount in the transaction.\\n event TransactionCreated(\\n uint256 indexed _transactionID,\\n address indexed _buyer,\\n address indexed _seller,\\n uint256 _amount\\n );\\n\\n /// @dev To be emitted when a transaction is resolved, either by its\\n /// execution, a timeout or because a ruling was enforced.\\n /// @param _transactionID The ID of the respective transaction.\\n /// @param _resolution Short description of what caused the transaction to be solved.\\n event TransactionResolved(uint256 indexed _transactionID, Resolution indexed _resolution);\\n\\n // ************************************* //\\n // * Function Modifiers * //\\n // ************************************* //\\n\\n modifier onlyByGovernor() {\\n if (governor != msg.sender) revert GovernorOnly();\\n _;\\n }\\n\\n // ************************************* //\\n // * Constructor * //\\n // ************************************* //\\n\\n /// @dev Constructor.\\n /// @param _arbitrator The arbitrator of the contract.\\n /// @param _arbitratorExtraData Extra data for the arbitrator.\\n /// @param _templateData The dispute template data.\\n /// @param _templateDataMappings The dispute template data mappings.\\n /// @param _templateRegistry The dispute template registry.\\n /// @param _feeTimeout Arbitration fee timeout for the parties.\\n constructor(\\n IArbitratorV2 _arbitrator,\\n bytes memory _arbitratorExtraData,\\n string memory _templateData,\\n string memory _templateDataMappings,\\n IDisputeTemplateRegistry _templateRegistry,\\n uint256 _feeTimeout\\n ) {\\n governor = msg.sender;\\n arbitrator = _arbitrator;\\n arbitratorExtraData = _arbitratorExtraData;\\n templateRegistry = _templateRegistry;\\n feeTimeout = _feeTimeout;\\n\\n templateId = templateRegistry.setDisputeTemplate(\\\"\\\", _templateData, _templateDataMappings);\\n }\\n\\n // ************************************* //\\n // * Governance * //\\n // ************************************* //\\n\\n function changeArbitrator(IArbitratorV2 _arbitrator) external onlyByGovernor {\\n arbitrator = _arbitrator;\\n }\\n\\n function changeArbitratorExtraData(bytes calldata _arbitratorExtraData) external onlyByGovernor {\\n arbitratorExtraData = _arbitratorExtraData;\\n }\\n\\n function changeTemplateRegistry(IDisputeTemplateRegistry _templateRegistry) external onlyByGovernor {\\n templateRegistry = _templateRegistry;\\n }\\n\\n function changeDisputeTemplate(\\n string memory _templateData,\\n string memory _templateDataMappings\\n ) external onlyByGovernor {\\n templateId = templateRegistry.setDisputeTemplate(\\\"\\\", _templateData, _templateDataMappings);\\n }\\n\\n // ************************************* //\\n // * State Modifiers * //\\n // ************************************* //\\n\\n /// @dev Create a transaction.\\n /// @param _timeoutPayment Time after which a party can automatically execute the arbitrable transaction.\\n /// @param _seller The recipient of the transaction.\\n /// @param _templateData The dispute template data.\\n /// @param _templateDataMappings The dispute template data mappings.\\n /// @return transactionID The index of the transaction.\\n function createTransaction(\\n uint256 _timeoutPayment,\\n address payable _seller,\\n string memory _templateData,\\n string memory _templateDataMappings\\n ) external payable returns (uint256 transactionID) {\\n Transaction storage transaction = transactions.push();\\n transaction.buyer = payable(msg.sender);\\n transaction.seller = _seller;\\n transaction.amount = msg.value;\\n transaction.deadline = block.timestamp + _timeoutPayment;\\n transaction.templateData = _templateData;\\n transaction.templateDataMappings = _templateDataMappings;\\n\\n transactionID = transactions.length - 1;\\n\\n emit TransactionCreated(transactionID, msg.sender, _seller, msg.value);\\n }\\n\\n /// @dev Pay seller. To be called if the good or service is provided.\\n /// @param _transactionID The index of the transaction.\\n /// @param _amount Amount to pay in wei.\\n function pay(uint256 _transactionID, uint256 _amount) external {\\n Transaction storage transaction = transactions[_transactionID];\\n if (transaction.buyer != msg.sender) revert BuyerOnly();\\n if (transaction.status != Status.NoDispute) revert TransactionDisputed();\\n if (_amount > transaction.amount) revert MaximumPaymentAmountExceeded();\\n\\n transaction.seller.send(_amount); // It is the user responsibility to accept ETH.\\n transaction.amount -= _amount;\\n\\n emit Payment(_transactionID, _amount, msg.sender);\\n }\\n\\n /// @dev Reimburse buyer. To be called if the good or service can't be fully provided.\\n /// @param _transactionID The index of the transaction.\\n /// @param _amountReimbursed Amount to reimburse in wei.\\n function reimburse(uint256 _transactionID, uint256 _amountReimbursed) external {\\n Transaction storage transaction = transactions[_transactionID];\\n if (transaction.seller != msg.sender) revert SellerOnly();\\n if (transaction.status != Status.NoDispute) revert TransactionDisputed();\\n if (_amountReimbursed > transaction.amount) revert MaximumPaymentAmountExceeded();\\n\\n transaction.buyer.send(_amountReimbursed); // It is the user responsibility to accept ETH.\\n transaction.amount -= _amountReimbursed;\\n\\n emit Payment(_transactionID, _amountReimbursed, msg.sender);\\n }\\n\\n /// @dev Transfer the transaction's amount to the seller if the timeout has passed.\\n /// @param _transactionID The index of the transaction.\\n function executeTransaction(uint256 _transactionID) external {\\n Transaction storage transaction = transactions[_transactionID];\\n if (block.timestamp < transaction.deadline) revert DeadlineNotPassed();\\n if (transaction.status != Status.NoDispute) revert TransactionDisputed();\\n\\n transaction.seller.send(transaction.amount); // It is the user responsibility to accept ETH.\\n transaction.amount = 0;\\n transaction.status = Status.TransactionResolved;\\n\\n emit TransactionResolved(_transactionID, Resolution.TransactionExecuted);\\n }\\n\\n /// @dev Pay the arbitration fee to raise a dispute. To be called by the buyer.\\n /// Note that the arbitrator can have createDispute throw, which will make\\n /// this function throw and therefore lead to a party being timed-out.\\n /// This is not a vulnerability as the arbitrator can rule in favor of one party anyway.\\n /// @param _transactionID The index of the transaction.\\n function payArbitrationFeeByBuyer(uint256 _transactionID) external payable {\\n Transaction storage transaction = transactions[_transactionID];\\n if (transaction.status >= Status.DisputeCreated) revert DisputeAlreadyCreatedOrTransactionAlreadyExecuted();\\n if (msg.sender != transaction.buyer) revert BuyerOnly();\\n\\n transaction.buyerFee += msg.value;\\n uint256 arbitrationCost = arbitrator.arbitrationCost(arbitratorExtraData);\\n if (transaction.buyerFee < arbitrationCost) revert BuyerFeeNotCoverArbitrationCosts();\\n\\n transaction.lastFeePaymentTime = block.timestamp;\\n\\n if (transaction.sellerFee < arbitrationCost) {\\n // The seller still has to pay. This can also happen if he has paid, but arbitrationCost has increased.\\n transaction.status = Status.WaitingSeller;\\n emit HasToPayFee(_transactionID, Party.Seller);\\n } else {\\n // The seller has also paid the fee. We create the dispute.\\n raiseDispute(_transactionID, arbitrationCost);\\n }\\n }\\n\\n /// @dev Pay the arbitration fee to raise a dispute. To be called by the seller.\\n /// Note that this function mirrors payArbitrationFeeByBuyer.\\n /// @param _transactionID The index of the transaction.\\n function payArbitrationFeeBySeller(uint256 _transactionID) external payable {\\n Transaction storage transaction = transactions[_transactionID];\\n if (transaction.status >= Status.DisputeCreated) revert DisputeAlreadyCreatedOrTransactionAlreadyExecuted();\\n if (msg.sender != transaction.seller) revert SellerOnly();\\n\\n transaction.sellerFee += msg.value;\\n uint256 arbitrationCost = arbitrator.arbitrationCost(arbitratorExtraData);\\n if (transaction.sellerFee < arbitrationCost) revert SellerFeeNotCoverArbitrationCosts();\\n\\n transaction.lastFeePaymentTime = block.timestamp;\\n\\n if (transaction.buyerFee < arbitrationCost) {\\n // The buyer still has to pay. This can also happen if he has paid, but arbitrationCost has increased.\\n transaction.status = Status.WaitingBuyer;\\n emit HasToPayFee(_transactionID, Party.Buyer);\\n } else {\\n // The buyer has also paid the fee. We create the dispute.\\n raiseDispute(_transactionID, arbitrationCost);\\n }\\n }\\n\\n /// @dev Reimburse buyer if seller fails to pay the fee.\\n /// @param _transactionID The index of the transaction.\\n function timeOutByBuyer(uint256 _transactionID) external {\\n Transaction storage transaction = transactions[_transactionID];\\n if (transaction.status != Status.WaitingSeller) revert NotWaitingForSellerFees();\\n if (block.timestamp - transaction.lastFeePaymentTime < feeTimeout) revert TimeoutNotPassed();\\n\\n if (transaction.sellerFee != 0) {\\n transaction.seller.send(transaction.sellerFee); // It is the user responsibility to accept ETH.\\n transaction.sellerFee = 0;\\n }\\n executeRuling(_transactionID, uint256(Party.Buyer));\\n emit TransactionResolved(_transactionID, Resolution.TimeoutByBuyer);\\n }\\n\\n /// @dev Pay seller if buyer fails to pay the fee.\\n /// @param _transactionID The index of the transaction.\\n function timeOutBySeller(uint256 _transactionID) external {\\n Transaction storage transaction = transactions[_transactionID];\\n if (transaction.status != Status.WaitingBuyer) revert NotWaitingForBuyerFees();\\n if (block.timestamp - transaction.lastFeePaymentTime < feeTimeout) revert TimeoutNotPassed();\\n\\n if (transaction.buyerFee != 0) {\\n transaction.buyer.send(transaction.buyerFee); // It is the user responsibility to accept ETH.\\n transaction.buyerFee = 0;\\n }\\n\\n executeRuling(_transactionID, uint256(Party.Seller));\\n emit TransactionResolved(_transactionID, Resolution.TimeoutBySeller);\\n }\\n\\n /// @dev Give a ruling for a dispute. Must be called by the arbitrator to enforce the final ruling.\\n /// The purpose of this function is to ensure that the address calling it has the right to rule on the contract.\\n /// @param _disputeID ID of the dispute in the Arbitrator contract.\\n /// @param _ruling Ruling given by the arbitrator. Note that 0 is reserved\\n /// for \\\"Refuse to arbitrate\\\".\\n function rule(uint256 _disputeID, uint256 _ruling) external override {\\n if (msg.sender != address(arbitrator)) revert ArbitratorOnly();\\n if (_ruling > AMOUNT_OF_CHOICES) revert InvalidRuling();\\n\\n uint256 transactionID = disputeIDtoTransactionID[_disputeID];\\n Transaction storage transaction = transactions[transactionID];\\n if (transaction.status != Status.DisputeCreated) revert DisputeAlreadyResolved();\\n\\n emit Ruling(arbitrator, _disputeID, _ruling);\\n executeRuling(transactionID, _ruling);\\n }\\n\\n // ************************************* //\\n // * Internal * //\\n // ************************************* //\\n\\n /// @dev Create a dispute.\\n /// @param _transactionID The index of the transaction.\\n /// @param _arbitrationCost Amount to pay the arbitrator.\\n function raiseDispute(uint256 _transactionID, uint256 _arbitrationCost) internal {\\n Transaction storage transaction = transactions[_transactionID];\\n transaction.status = Status.DisputeCreated;\\n transaction.disputeID = arbitrator.createDispute{value: _arbitrationCost}(\\n AMOUNT_OF_CHOICES,\\n arbitratorExtraData\\n );\\n disputeIDtoTransactionID[transaction.disputeID] = _transactionID;\\n emit DisputeRequest(arbitrator, transaction.disputeID, _transactionID, templateId, \\\"\\\");\\n\\n // Refund buyer if he overpaid.\\n if (transaction.buyerFee > _arbitrationCost) {\\n uint256 extraFeeBuyer = transaction.buyerFee - _arbitrationCost;\\n transaction.buyerFee = _arbitrationCost;\\n transaction.buyer.send(extraFeeBuyer); // It is the user responsibility to accept ETH.\\n }\\n\\n // Refund seller if he overpaid.\\n if (transaction.sellerFee > _arbitrationCost) {\\n uint256 extraFeeSeller = transaction.sellerFee - _arbitrationCost;\\n transaction.sellerFee = _arbitrationCost;\\n transaction.seller.send(extraFeeSeller); // It is the user responsibility to accept ETH.\\n }\\n }\\n\\n /// @dev Execute a ruling of a dispute. It reimburses the fee to the winning party.\\n /// @param _transactionID The index of the transaction.\\n /// @param _ruling Ruling given by the arbitrator. 1 : Reimburse the seller. 2 : Pay the buyer.\\n function executeRuling(uint256 _transactionID, uint256 _ruling) internal {\\n Transaction storage transaction = transactions[_transactionID];\\n // Give the arbitration fee back.\\n // Note that we use send to prevent a party from blocking the execution.\\n if (_ruling == uint256(Party.Buyer)) {\\n transaction.buyer.send(transaction.buyerFee + transaction.amount);\\n } else if (_ruling == uint256(Party.Seller)) {\\n transaction.seller.send(transaction.sellerFee + transaction.amount);\\n } else {\\n uint256 splitAmount = (transaction.buyerFee + transaction.amount) / 2;\\n transaction.buyer.send(splitAmount);\\n transaction.seller.send(splitAmount);\\n }\\n\\n transaction.amount = 0;\\n transaction.buyerFee = 0;\\n transaction.sellerFee = 0;\\n transaction.status = Status.TransactionResolved;\\n\\n emit TransactionResolved(_transactionID, Resolution.RulingEnforced);\\n }\\n\\n // ************************************* //\\n // * Public Views * //\\n // ************************************* //\\n\\n /// @dev Getter to know the count of transactions.\\n /// @return The count of transactions.\\n function getCountTransactions() external view returns (uint256) {\\n return transactions.length;\\n }\\n\\n // ************************************* //\\n // * Errors * //\\n // ************************************* //\\n\\n error GovernorOnly();\\n error BuyerOnly();\\n error SellerOnly();\\n error ArbitratorOnly();\\n error TransactionDisputed();\\n error MaximumPaymentAmountExceeded();\\n error DisputeAlreadyCreatedOrTransactionAlreadyExecuted();\\n error DeadlineNotPassed();\\n error BuyerFeeNotCoverArbitrationCosts();\\n error SellerFeeNotCoverArbitrationCosts();\\n error NotWaitingForSellerFees();\\n error NotWaitingForBuyerFees();\\n error TimeoutNotPassed();\\n error InvalidRuling();\\n error DisputeAlreadyResolved();\\n}\\n\",\"keccak256\":\"0x9fa34c5dca0b60c55a0ab61601f78a38c599b95bb04652cc11aab399431367fe\",\"license\":\"MIT\"},\"src/arbitration/interfaces/IArbitrableV2.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n\\npragma solidity 0.8.18;\\n\\nimport \\\"./IArbitratorV2.sol\\\";\\n\\n/// @title IArbitrableV2\\n/// @notice Arbitrable interface.\\n/// When developing arbitrable contracts, we need to:\\n/// - Define the action taken when a ruling is received by the contract.\\n/// - Allow dispute creation. For this a function must call arbitrator.createDispute{value: _fee}(_choices,_extraData);\\ninterface IArbitrableV2 {\\n /// @dev To be emitted when a dispute is created to link the correct meta-evidence to the disputeID.\\n /// @param _arbitrator The arbitrator of the contract.\\n /// @param _arbitrableDisputeID The identifier of the dispute in the Arbitrable contract.\\n /// @param _externalDisputeID An identifier created outside Kleros by the protocol requesting arbitration.\\n /// @param _templateId The identifier of the dispute template. Should not be used with _templateUri.\\n /// @param _templateUri The URI to the dispute template. For example on IPFS: starting with '/ipfs/'. Should not be used with _templateId.\\n event DisputeRequest(\\n IArbitratorV2 indexed _arbitrator,\\n uint256 indexed _arbitrableDisputeID,\\n uint256 _externalDisputeID,\\n uint256 _templateId,\\n string _templateUri\\n );\\n\\n /// @dev To be raised when a ruling is given.\\n /// @param _arbitrator The arbitrator giving the ruling.\\n /// @param _disputeID The identifier of the dispute in the Arbitrator contract.\\n /// @param _ruling The ruling which was given.\\n event Ruling(IArbitratorV2 indexed _arbitrator, uint256 indexed _disputeID, uint256 _ruling);\\n\\n /// @dev Give a ruling for a dispute.\\n /// Must be called by the arbitrator.\\n /// The purpose of this function is to ensure that the address calling it has the right to rule on the contract.\\n /// @param _disputeID The identifier of the dispute in the Arbitrator contract.\\n /// @param _ruling Ruling given by the arbitrator.\\n /// Note that 0 is reserved for \\\"Not able/wanting to make a decision\\\".\\n function rule(uint256 _disputeID, uint256 _ruling) external;\\n}\\n\",\"keccak256\":\"0x389326b1f749454ed179bdac2f9d6ce24a1ef944bbce976ca78b93f4e173354a\",\"license\":\"MIT\"},\"src/arbitration/interfaces/IArbitratorV2.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n\\npragma solidity 0.8.18;\\n\\nimport \\\"@openzeppelin/contracts/token/ERC20/IERC20.sol\\\";\\nimport \\\"./IArbitrableV2.sol\\\";\\n\\n/// @title Arbitrator\\n/// Arbitrator interface that implements the new arbitration standard.\\n/// Unlike the ERC-792 this standard is not concerned with appeals, so each arbitrator can implement an appeal system that suits it the most.\\n/// When developing arbitrator contracts we need to:\\n/// - Define the functions for dispute creation (createDispute). Don't forget to store the arbitrated contract and the disputeID (which should be unique, may nbDisputes).\\n/// - Define the functions for cost display (arbitrationCost).\\n/// - Allow giving rulings. For this a function must call arbitrable.rule(disputeID, ruling).\\ninterface IArbitratorV2 {\\n /// @dev To be emitted when a dispute is created.\\n /// @param _disputeID The identifier of the dispute in the Arbitrator contract.\\n /// @param _arbitrable The contract which created the dispute.\\n event DisputeCreation(uint256 indexed _disputeID, IArbitrableV2 indexed _arbitrable);\\n\\n /// @dev To be raised when a ruling is given.\\n /// @param _arbitrable The arbitrable receiving the ruling.\\n /// @param _disputeID The identifier of the dispute in the Arbitrator contract.\\n /// @param _ruling The ruling which was given.\\n event Ruling(IArbitrableV2 indexed _arbitrable, uint256 indexed _disputeID, uint256 _ruling);\\n\\n /// @dev To be emitted when an ERC20 token is added or removed as a method to pay fees.\\n /// @param _token The ERC20 token.\\n /// @param _accepted Whether the token is accepted or not.\\n event AcceptedFeeToken(IERC20 indexed _token, bool indexed _accepted);\\n\\n /// @dev To be emitted when the fee for a particular ERC20 token is updated.\\n /// @param _feeToken The ERC20 token.\\n /// @param _rateInEth The new rate of the fee token in ETH.\\n /// @param _rateDecimals The new decimals of the fee token rate.\\n event NewCurrencyRate(IERC20 indexed _feeToken, uint64 _rateInEth, uint8 _rateDecimals);\\n\\n /// @dev Create a dispute and pay for the fees in the native currency, typically ETH.\\n /// Must be called by the arbitrable contract.\\n /// Must pay at least arbitrationCost(_extraData).\\n /// @param _numberOfChoices The number of choices the arbitrator can choose from in this dispute.\\n /// @param _extraData Additional info about the dispute. We use it to pass the ID of the dispute's court (first 32 bytes), the minimum number of jurors required (next 32 bytes) and the ID of the specific dispute kit (last 32 bytes).\\n /// @return disputeID The identifier of the dispute created.\\n function createDispute(\\n uint256 _numberOfChoices,\\n bytes calldata _extraData\\n ) external payable returns (uint256 disputeID);\\n\\n /// @dev Create a dispute and pay for the fees in a supported ERC20 token.\\n /// Must be called by the arbitrable contract.\\n /// Must pay at least arbitrationCost(_extraData).\\n /// @param _numberOfChoices The number of choices the arbitrator can choose from in this dispute.\\n /// @param _extraData Additional info about the dispute. We use it to pass the ID of the dispute's court (first 32 bytes), the minimum number of jurors required (next 32 bytes) and the ID of the specific dispute kit (last 32 bytes).\\n /// @param _feeToken The ERC20 token used to pay fees.\\n /// @param _feeAmount Amount of the ERC20 token used to pay fees.\\n /// @return disputeID The identifier of the dispute created.\\n function createDispute(\\n uint256 _numberOfChoices,\\n bytes calldata _extraData,\\n IERC20 _feeToken,\\n uint256 _feeAmount\\n ) external returns (uint256 disputeID);\\n\\n /// @dev Compute the cost of arbitration denominated in the native currency, typically ETH.\\n /// It is recommended not to increase it often, as it can be highly time and gas consuming for the arbitrated contracts to cope with fee augmentation.\\n /// @param _extraData Additional info about the dispute. We use it to pass the ID of the dispute's court (first 32 bytes), the minimum number of jurors required (next 32 bytes) and the ID of the specific dispute kit (last 32 bytes).\\n /// @return cost The arbitration cost in ETH.\\n function arbitrationCost(bytes calldata _extraData) external view returns (uint256 cost);\\n\\n /// @dev Compute the cost of arbitration denominated in `_feeToken`.\\n /// It is recommended not to increase it often, as it can be highly time and gas consuming for the arbitrated contracts to cope with fee augmentation.\\n /// @param _extraData Additional info about the dispute. We use it to pass the ID of the dispute's court (first 32 bytes), the minimum number of jurors required (next 32 bytes) and the ID of the specific dispute kit (last 32 bytes).\\n /// @param _feeToken The ERC20 token used to pay fees.\\n /// @return cost The arbitration cost in `_feeToken`.\\n function arbitrationCost(bytes calldata _extraData, IERC20 _feeToken) external view returns (uint256 cost);\\n\\n /// @dev Gets the current ruling of a specified dispute.\\n /// @param _disputeID The ID of the dispute.\\n /// @return ruling The current ruling.\\n /// @return tied Whether it's a tie or not.\\n /// @return overridden Whether the ruling was overridden by appeal funding or not.\\n function currentRuling(uint256 _disputeID) external view returns (uint256 ruling, bool tied, bool overridden);\\n}\\n\",\"keccak256\":\"0x453943ba5ccc94b9b9cdfd4afd3678682d62d8b90fe16b43e90215387d2f6a51\",\"license\":\"MIT\"},\"src/arbitration/interfaces/IDisputeTemplateRegistry.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n\\npragma solidity 0.8.18;\\n\\n/// @title IDisputeTemplate\\n/// @notice Dispute Template interface.\\ninterface IDisputeTemplateRegistry {\\n /// @dev To be emitted when a new dispute template is created.\\n /// @param _templateId The identifier of the dispute template.\\n /// @param _templateTag An optional tag for the dispute template, such as \\\"registration\\\" or \\\"removal\\\".\\n /// @param _templateData The template data.\\n /// @param _templateDataMappings The data mappings.\\n event DisputeTemplate(\\n uint256 indexed _templateId,\\n string indexed _templateTag,\\n string _templateData,\\n string _templateDataMappings\\n );\\n\\n function setDisputeTemplate(\\n string memory _templateTag,\\n string memory _templateData,\\n string memory _templateDataMappings\\n ) external returns (uint256 templateId);\\n}\\n\",\"keccak256\":\"0x88b0038d226532e6cf862a485d162f7bca61ac3d361d6801146b55a240f091ac\",\"license\":\"MIT\"}},\"version\":1}", - "bytecode": "0x60c06040523480156200001157600080fd5b50604051620022c0380380620022c083398101604081905262000034916200020d565b33608052600080546001600160a01b0319166001600160a01b038816179055600162000061868262000376565b50600280546001600160a01b0319166001600160a01b03841690811790915560a08290526040516312a6505d60e21b8152634a99417490620000aa908790879060040162000470565b6020604051808303816000875af1158015620000ca573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620000f09190620004b0565b60035550620004ca945050505050565b6001600160a01b03811681146200011657600080fd5b50565b634e487b7160e01b600052604160045260246000fd5b60005b838110156200014c57818101518382015260200162000132565b50506000910152565b60006001600160401b038084111562000172576200017262000119565b604051601f8501601f19908116603f011681019082821181831017156200019d576200019d62000119565b81604052809350858152868686011115620001b757600080fd5b620001c78660208301876200012f565b5050509392505050565b600082601f830112620001e357600080fd5b620001f48383516020850162000155565b9392505050565b8051620002088162000100565b919050565b60008060008060008060c087890312156200022757600080fd5b8651620002348162000100565b60208801519096506001600160401b03808211156200025257600080fd5b818901915089601f8301126200026757600080fd5b620002788a83516020850162000155565b965060408901519150808211156200028f57600080fd5b6200029d8a838b01620001d1565b95506060890151915080821115620002b457600080fd5b50620002c389828a01620001d1565b935050620002d460808801620001fb565b915060a087015190509295509295509295565b600181811c90821680620002fc57607f821691505b6020821081036200031d57634e487b7160e01b600052602260045260246000fd5b50919050565b601f8211156200037157600081815260208120601f850160051c810160208610156200034c5750805b601f850160051c820191505b818110156200036d5782815560010162000358565b5050505b505050565b81516001600160401b0381111562000392576200039262000119565b620003aa81620003a38454620002e7565b8462000323565b602080601f831160018114620003e25760008415620003c95750858301515b600019600386901b1c1916600185901b1785556200036d565b600085815260208120601f198616915b828110156200041357888601518255948401946001909101908401620003f2565b5085821015620004325787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b600081518084526200045c8160208601602086016200012f565b601f01601f19169290920160200192915050565b606081526000606082015260806020820152600062000493608083018562000442565b8281036040840152620004a7818562000442565b95945050505050565b600060208284031215620004c357600080fd5b5051919050565b60805160a051611da66200051a60003960008181610364015281816105490152610f16015260008181610157015281816108d201528181610abd01528181610cb501526111be0152611da66000f3fe6080604052600436106101405760003560e01c80637aa77f29116100b6578063d1e41b5d1161006f578063d1e41b5d146103a6578063e77d0bd3146103b9578063ee22610b146103d9578063ef48eee6146103f9578063fc548f0814610419578063fe43a9921461043957600080fd5b80637aa77f29146102d05780639ace38c2146102e6578063a0af81f01461031d578063a527aa6a1461033d578063b329036b14610352578063c5d552881461038657600080fd5b80632fbe3b03116101085780632fbe3b0314610210578063311a6c561461023d57806334e2672d1461025d5780633bf547241461027d5780634660ebbe146102905780636cc6cde1146102b057600080fd5b80630c340a24146101455780630c7ac7b6146101965780631bd1823a146101b85780632be6d005146101da5780632e0b6422146101ed575b600080fd5b34801561015157600080fd5b506101797f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020015b60405180910390f35b3480156101a257600080fd5b506101ab610459565b60405161018d919061169b565b3480156101c457600080fd5b506101d86101d33660046116b5565b6104e7565b005b6101d86101e83660046116b5565b610612565b3480156101f957600080fd5b50610202600281565b60405190815260200161018d565b34801561021c57600080fd5b5061020261022b3660046116b5565b60056020526000908152604090205481565b34801561024957600080fd5b506101d86102583660046116ce565b6107c0565b34801561026957600080fd5b506101d86102783660046116f0565b6108d0565b6101d861028b3660046116b5565b610926565b34801561029c57600080fd5b506101d86102ab36600461177a565b610abb565b3480156102bc57600080fd5b50600054610179906001600160a01b031681565b3480156102dc57600080fd5b5061020260035481565b3480156102f257600080fd5b506103066103013660046116b5565b610b26565b60405161018d9b9a999897969594939291906117ad565b34801561032957600080fd5b50600254610179906001600160a01b031681565b34801561034957600080fd5b50600454610202565b34801561035e57600080fd5b506102027f000000000000000000000000000000000000000000000000000000000000000081565b34801561039257600080fd5b506101d86103a13660046118e0565b610cb3565b6102026103b4366004611944565b610d78565b3480156103c557600080fd5b506101d86103d43660046116b5565b610eb4565b3480156103e557600080fd5b506101d86103f43660046116b5565b610fb7565b34801561040557600080fd5b506101d86104143660046116ce565b611088565b34801561042557600080fd5b506101d861043436600461177a565b6111bc565b34801561044557600080fd5b506101d86104543660046116ce565b611227565b60018054610466906119c4565b80601f0160208091040260200160405190810160405280929190818152602001828054610492906119c4565b80156104df5780601f106104b4576101008083540402835291602001916104df565b820191906000526020600020905b8154815290600101906020018083116104c257829003601f168201915b505050505081565b6000600482815481106104fc576104fc6119fe565b60009182526020909120600b9091020190506001600a82015460ff16600481111561052957610529611797565b1461054757604051635ed7670b60e11b815260040160405180910390fd5b7f00000000000000000000000000000000000000000000000000000000000000008160070154426105789190611a2a565b101561059757604051634799187b60e01b815260040160405180910390fd5b6005810154156105d557805460058201546040516001600160a01b039092169181156108fc0291906000818181858888f15050600060058501555050505b6105e0826002611318565b60025b60405183907f89e6168584e1de3ec704ff46376271b5481b0d9b2b0ef0ae102f0d001093e00090600090a35050565b600060048281548110610627576106276119fe565b60009182526020909120600b9091020190506003600a82015460ff16600481111561065457610654611797565b106106725760405163df44e9d960e01b815260040160405180910390fd5b80546001600160a01b0316331461069c5760405163e2bc376b60e01b815260040160405180910390fd5b348160050160008282546106b09190611a43565b90915550506000805460405163f7434ea960e01b81526001600160a01b039091169063f7434ea9906106e790600190600401611ad3565b602060405180830381865afa158015610704573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107289190611ae6565b9050808260050154101561074f57604051631a48f3db60e11b815260040160405180910390fd5b42600783015560068201548111156107b157600a8201805460ff1916600290811790915560405184917fc74b9f7dedf2887cd3c113b6d8da9cea19e55c1116e25f1f0e1b72d7543179b5916107a49190611aff565b60405180910390a2505050565b6107bb8382611488565b505050565b6000546001600160a01b031633146107eb57604051630955f84760e31b815260040160405180910390fd5b600281111561080d576040516309efd47960e41b815260040160405180910390fd5b6000828152600560205260408120546004805491929183908110610833576108336119fe565b60009182526020909120600b9091020190506003600a82015460ff16600481111561086057610860611797565b1461087e5760405163f10068b560e01b815260040160405180910390fd5b60005460405184815285916001600160a01b0316907f394027a5fa6e098a1191094d1719d6929b9abc535fcc0c8f448d6a4e756222769060200160405180910390a36108ca8284611318565b50505050565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031633146109195760405163c383977560e01b815260040160405180910390fd5b60016107bb828483611b7c565b60006004828154811061093b5761093b6119fe565b60009182526020909120600b9091020190506003600a82015460ff16600481111561096857610968611797565b106109865760405163df44e9d960e01b815260040160405180910390fd5b60018101546001600160a01b031633146109b357604051635800797f60e11b815260040160405180910390fd5b348160060160008282546109c79190611a43565b90915550506000805460405163f7434ea960e01b81526001600160a01b039091169063f7434ea9906109fe90600190600401611ad3565b602060405180830381865afa158015610a1b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a3f9190611ae6565b90508082600601541015610a665760405163818d938f60e01b815260040160405180910390fd5b42600783015560058201548111156107b157600a8201805460ff1916600190811790915560405184917fc74b9f7dedf2887cd3c113b6d8da9cea19e55c1116e25f1f0e1b72d7543179b5916107a49190611aff565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03163314610b045760405163c383977560e01b815260040160405180910390fd5b600280546001600160a01b0319166001600160a01b0392909216919091179055565b60048181548110610b3657600080fd5b60009182526020909120600b90910201805460018201546002830154600384015460048501546005860154600687015460078801546008890180546001600160a01b03998a169b50989097169895979496939592949193909291610b99906119c4565b80601f0160208091040260200160405190810160405280929190818152602001828054610bc5906119c4565b8015610c125780601f10610be757610100808354040283529160200191610c12565b820191906000526020600020905b815481529060010190602001808311610bf557829003601f168201915b505050505090806009018054610c27906119c4565b80601f0160208091040260200160405190810160405280929190818152602001828054610c53906119c4565b8015610ca05780601f10610c7557610100808354040283529160200191610ca0565b820191906000526020600020905b815481529060010190602001808311610c8357829003601f168201915b505050600a909301549192505060ff168b565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03163314610cfc5760405163c383977560e01b815260040160405180910390fd5b6002546040516312a6505d60e21b81526001600160a01b0390911690634a99417490610d2e9085908590600401611c37565b6020604051808303816000875af1158015610d4d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d719190611ae6565b6003555050565b600480546001810182556000918252600b027f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b810180546001600160a01b0319908116331782557f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19c830180546001600160a01b0389169216919091179055347f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19d90920191909155610e298642611a43565b600382015560088101610e3c8582611c73565b5060098101610e4b8482611c73565b50600454610e5b90600190611a2a565b9150846001600160a01b0316336001600160a01b0316837fe9097a4f4eddc0e5906640fcd9e1193c9db52771536ca4c8b06ab4c40aa045d234604051610ea391815260200190565b60405180910390a450949350505050565b600060048281548110610ec957610ec96119fe565b60009182526020909120600b9091020190506002600a82015460ff166004811115610ef657610ef6611797565b14610f1457604051638225aba560e01b815260040160405180910390fd5b7f0000000000000000000000000000000000000000000000000000000000000000816007015442610f459190611a2a565b1015610f6457604051634799187b60e01b815260040160405180910390fd5b600681015415610fa557600181015460068201546040516001600160a01b039092169181156108fc0291906000818181858888f15050600060068501555050505b610fb0826001611318565b60016105e3565b600060048281548110610fcc57610fcc6119fe565b90600052602060002090600b020190508060030154421015611001576040516302eb354360e41b815260040160405180910390fd5b6000600a82015460ff16600481111561101c5761101c611797565b1461103a57604051634e22597f60e11b815260040160405180910390fd5b600181015460028201546040516001600160a01b039092169181156108fc0291906000818181858888f15050600060028501819055600a8501805460ff1916600417905592506105e3915050565b60006004838154811061109d5761109d6119fe565b60009182526020909120600b9091020180549091506001600160a01b031633146110da5760405163e2bc376b60e01b815260040160405180910390fd5b6000600a82015460ff1660048111156110f5576110f5611797565b1461111357604051634e22597f60e11b815260040160405180910390fd5b8060020154821115611138576040516305aafecf60e01b815260040160405180910390fd5b60018101546040516001600160a01b039091169083156108fc029084906000818181858888f1935050505050818160020160008282546111789190611a2a565b90915550506040805183815233602082015284917fd1432ca9a38d944f01b256a411861b109bc4bfe200c40d7144e919a16b86a8a8910160405180910390a2505050565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031633146112055760405163c383977560e01b815260040160405180910390fd5b600080546001600160a01b0319166001600160a01b0392909216919091179055565b60006004838154811061123c5761123c6119fe565b600091825260209091206001600b90920201908101549091506001600160a01b0316331461127d57604051635800797f60e11b815260040160405180910390fd5b6000600a82015460ff16600481111561129857611298611797565b146112b657604051634e22597f60e11b815260040160405180910390fd5b80600201548211156112db576040516305aafecf60e01b815260040160405180910390fd5b80546040516001600160a01b039091169083156108fc029084906000818181858888f1935050505050818160020160008282546111789190611a2a565b60006004838154811061132d5761132d6119fe565b60009182526020909120600b90910201905060018203611389578054600282015460058301546001600160a01b03909216916108fc9161136c91611a43565b6040518115909202916000818181858888f1935050505050611431565b600282036113b9576001810154600282015460068301546001600160a01b03909216916108fc9161136c91611a43565b60006002826002015483600501546113d19190611a43565b6113db9190611d2d565b82546040519192506001600160a01b03169082156108fc029083906000818181858888f150505060018401546040516001600160a01b03909116925083156108fc02915083906000818181858888f15050505050505b6000600282018190556005820181905560068201819055600a8201805460ff1916600417905560405160039185917f89e6168584e1de3ec704ff46376271b5481b0d9b2b0ef0ae102f0d001093e0009190a3505050565b60006004838154811061149d5761149d6119fe565b600091825260208220600a600b90920201908101805460ff19166003179055905460405163c13517e160e01b81529192506001600160a01b03169063c13517e19084906114f290600290600190600401611d4f565b60206040518083038185885af1158015611510573d6000803e3d6000fd5b50505050506040513d601f19601f820116820180604052508101906115359190611ae6565b600482018181556000918252600560205260408083208690559054915460035491516001600160a01b03909116917f8bd32f430ff060e6bd204709b3790c9807987263d3230c580dc80b5f89e27186916115a691888252602082015260606040820181905260009082015260800190565b60405180910390a381816005015411156115fe5760008282600501546115cc9190611a2a565b6005830184905582546040519192506001600160a01b03169082156108fc029083906000818181858888f15050505050505b81816006015411156107bb57600082826006015461161c9190611a2a565b6006830184905560018301546040519192506001600160a01b03169082156108fc029083906000818181858888f1505050505050505050565b6000815180845260005b8181101561167b5760208185018101518683018201520161165f565b506000602082860101526020601f19601f83011685010191505092915050565b6020815260006116ae6020830184611655565b9392505050565b6000602082840312156116c757600080fd5b5035919050565b600080604083850312156116e157600080fd5b50508035926020909101359150565b6000806020838503121561170357600080fd5b823567ffffffffffffffff8082111561171b57600080fd5b818501915085601f83011261172f57600080fd5b81358181111561173e57600080fd5b86602082850101111561175057600080fd5b60209290920196919550909350505050565b6001600160a01b038116811461177757600080fd5b50565b60006020828403121561178c57600080fd5b81356116ae81611762565b634e487b7160e01b600052602160045260246000fd5b600061016060018060a01b03808f168452808e166020850152508b60408401528a60608401528960808401528860a08401528760c08401528660e0840152806101008401526117fe81840187611655565b90508281036101208401526118138186611655565b9150506005831061182657611826611797565b826101408301529c9b505050505050505050505050565b634e487b7160e01b600052604160045260246000fd5b600082601f83011261186457600080fd5b813567ffffffffffffffff8082111561187f5761187f61183d565b604051601f8301601f19908116603f011681019082821181831017156118a7576118a761183d565b816040528381528660208588010111156118c057600080fd5b836020870160208301376000602085830101528094505050505092915050565b600080604083850312156118f357600080fd5b823567ffffffffffffffff8082111561190b57600080fd5b61191786838701611853565b9350602085013591508082111561192d57600080fd5b5061193a85828601611853565b9150509250929050565b6000806000806080858703121561195a57600080fd5b84359350602085013561196c81611762565b9250604085013567ffffffffffffffff8082111561198957600080fd5b61199588838901611853565b935060608701359150808211156119ab57600080fd5b506119b887828801611853565b91505092959194509250565b600181811c908216806119d857607f821691505b6020821081036119f857634e487b7160e01b600052602260045260246000fd5b50919050565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b81810381811115611a3d57611a3d611a14565b92915050565b80820180821115611a3d57611a3d611a14565b60008154611a63816119c4565b808552602060018381168015611a805760018114611a9a57611ac8565b60ff1985168884015283151560051b880183019550611ac8565b866000528260002060005b85811015611ac05781548a8201860152908301908401611aa5565b890184019650505b505050505092915050565b6020815260006116ae6020830184611a56565b600060208284031215611af857600080fd5b5051919050565b6020810160038310611b1357611b13611797565b91905290565b601f8211156107bb57600081815260208120601f850160051c81016020861015611b405750805b601f850160051c820191505b81811015611b5f57828155600101611b4c565b505050505050565b600019600383901b1c191660019190911b1790565b67ffffffffffffffff831115611b9457611b9461183d565b611ba883611ba283546119c4565b83611b19565b6000601f841160018114611bd65760008515611bc45750838201355b611bce8682611b67565b845550611c30565b600083815260209020601f19861690835b82811015611c075786850135825560209485019460019092019101611be7565b5086821015611c245760001960f88860031b161c19848701351681555b505060018560011b0183555b5050505050565b6060815260006060820152608060208201526000611c586080830185611655565b8281036040840152611c6a8185611655565b95945050505050565b815167ffffffffffffffff811115611c8d57611c8d61183d565b611ca181611c9b84546119c4565b84611b19565b602080601f831160018114611cd05760008415611cbe5750858301515b611cc88582611b67565b865550611b5f565b600085815260208120601f198616915b82811015611cff57888601518255948401946001909101908401611ce0565b5085821015611d1d5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b600082611d4a57634e487b7160e01b600052601260045260246000fd5b500490565b828152604060208201526000611d686040830184611a56565b94935050505056fea264697066735822122005b0699f8f6e4a5a2b7331292d34d72ef79a72fae2d7ed4c1c12fe8da221be2b64736f6c63430008120033", - "deployedBytecode": "0x6080604052600436106101405760003560e01c80637aa77f29116100b6578063d1e41b5d1161006f578063d1e41b5d146103a6578063e77d0bd3146103b9578063ee22610b146103d9578063ef48eee6146103f9578063fc548f0814610419578063fe43a9921461043957600080fd5b80637aa77f29146102d05780639ace38c2146102e6578063a0af81f01461031d578063a527aa6a1461033d578063b329036b14610352578063c5d552881461038657600080fd5b80632fbe3b03116101085780632fbe3b0314610210578063311a6c561461023d57806334e2672d1461025d5780633bf547241461027d5780634660ebbe146102905780636cc6cde1146102b057600080fd5b80630c340a24146101455780630c7ac7b6146101965780631bd1823a146101b85780632be6d005146101da5780632e0b6422146101ed575b600080fd5b34801561015157600080fd5b506101797f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020015b60405180910390f35b3480156101a257600080fd5b506101ab610459565b60405161018d919061169b565b3480156101c457600080fd5b506101d86101d33660046116b5565b6104e7565b005b6101d86101e83660046116b5565b610612565b3480156101f957600080fd5b50610202600281565b60405190815260200161018d565b34801561021c57600080fd5b5061020261022b3660046116b5565b60056020526000908152604090205481565b34801561024957600080fd5b506101d86102583660046116ce565b6107c0565b34801561026957600080fd5b506101d86102783660046116f0565b6108d0565b6101d861028b3660046116b5565b610926565b34801561029c57600080fd5b506101d86102ab36600461177a565b610abb565b3480156102bc57600080fd5b50600054610179906001600160a01b031681565b3480156102dc57600080fd5b5061020260035481565b3480156102f257600080fd5b506103066103013660046116b5565b610b26565b60405161018d9b9a999897969594939291906117ad565b34801561032957600080fd5b50600254610179906001600160a01b031681565b34801561034957600080fd5b50600454610202565b34801561035e57600080fd5b506102027f000000000000000000000000000000000000000000000000000000000000000081565b34801561039257600080fd5b506101d86103a13660046118e0565b610cb3565b6102026103b4366004611944565b610d78565b3480156103c557600080fd5b506101d86103d43660046116b5565b610eb4565b3480156103e557600080fd5b506101d86103f43660046116b5565b610fb7565b34801561040557600080fd5b506101d86104143660046116ce565b611088565b34801561042557600080fd5b506101d861043436600461177a565b6111bc565b34801561044557600080fd5b506101d86104543660046116ce565b611227565b60018054610466906119c4565b80601f0160208091040260200160405190810160405280929190818152602001828054610492906119c4565b80156104df5780601f106104b4576101008083540402835291602001916104df565b820191906000526020600020905b8154815290600101906020018083116104c257829003601f168201915b505050505081565b6000600482815481106104fc576104fc6119fe565b60009182526020909120600b9091020190506001600a82015460ff16600481111561052957610529611797565b1461054757604051635ed7670b60e11b815260040160405180910390fd5b7f00000000000000000000000000000000000000000000000000000000000000008160070154426105789190611a2a565b101561059757604051634799187b60e01b815260040160405180910390fd5b6005810154156105d557805460058201546040516001600160a01b039092169181156108fc0291906000818181858888f15050600060058501555050505b6105e0826002611318565b60025b60405183907f89e6168584e1de3ec704ff46376271b5481b0d9b2b0ef0ae102f0d001093e00090600090a35050565b600060048281548110610627576106276119fe565b60009182526020909120600b9091020190506003600a82015460ff16600481111561065457610654611797565b106106725760405163df44e9d960e01b815260040160405180910390fd5b80546001600160a01b0316331461069c5760405163e2bc376b60e01b815260040160405180910390fd5b348160050160008282546106b09190611a43565b90915550506000805460405163f7434ea960e01b81526001600160a01b039091169063f7434ea9906106e790600190600401611ad3565b602060405180830381865afa158015610704573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107289190611ae6565b9050808260050154101561074f57604051631a48f3db60e11b815260040160405180910390fd5b42600783015560068201548111156107b157600a8201805460ff1916600290811790915560405184917fc74b9f7dedf2887cd3c113b6d8da9cea19e55c1116e25f1f0e1b72d7543179b5916107a49190611aff565b60405180910390a2505050565b6107bb8382611488565b505050565b6000546001600160a01b031633146107eb57604051630955f84760e31b815260040160405180910390fd5b600281111561080d576040516309efd47960e41b815260040160405180910390fd5b6000828152600560205260408120546004805491929183908110610833576108336119fe565b60009182526020909120600b9091020190506003600a82015460ff16600481111561086057610860611797565b1461087e5760405163f10068b560e01b815260040160405180910390fd5b60005460405184815285916001600160a01b0316907f394027a5fa6e098a1191094d1719d6929b9abc535fcc0c8f448d6a4e756222769060200160405180910390a36108ca8284611318565b50505050565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031633146109195760405163c383977560e01b815260040160405180910390fd5b60016107bb828483611b7c565b60006004828154811061093b5761093b6119fe565b60009182526020909120600b9091020190506003600a82015460ff16600481111561096857610968611797565b106109865760405163df44e9d960e01b815260040160405180910390fd5b60018101546001600160a01b031633146109b357604051635800797f60e11b815260040160405180910390fd5b348160060160008282546109c79190611a43565b90915550506000805460405163f7434ea960e01b81526001600160a01b039091169063f7434ea9906109fe90600190600401611ad3565b602060405180830381865afa158015610a1b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a3f9190611ae6565b90508082600601541015610a665760405163818d938f60e01b815260040160405180910390fd5b42600783015560058201548111156107b157600a8201805460ff1916600190811790915560405184917fc74b9f7dedf2887cd3c113b6d8da9cea19e55c1116e25f1f0e1b72d7543179b5916107a49190611aff565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03163314610b045760405163c383977560e01b815260040160405180910390fd5b600280546001600160a01b0319166001600160a01b0392909216919091179055565b60048181548110610b3657600080fd5b60009182526020909120600b90910201805460018201546002830154600384015460048501546005860154600687015460078801546008890180546001600160a01b03998a169b50989097169895979496939592949193909291610b99906119c4565b80601f0160208091040260200160405190810160405280929190818152602001828054610bc5906119c4565b8015610c125780601f10610be757610100808354040283529160200191610c12565b820191906000526020600020905b815481529060010190602001808311610bf557829003601f168201915b505050505090806009018054610c27906119c4565b80601f0160208091040260200160405190810160405280929190818152602001828054610c53906119c4565b8015610ca05780601f10610c7557610100808354040283529160200191610ca0565b820191906000526020600020905b815481529060010190602001808311610c8357829003601f168201915b505050600a909301549192505060ff168b565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03163314610cfc5760405163c383977560e01b815260040160405180910390fd5b6002546040516312a6505d60e21b81526001600160a01b0390911690634a99417490610d2e9085908590600401611c37565b6020604051808303816000875af1158015610d4d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d719190611ae6565b6003555050565b600480546001810182556000918252600b027f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b810180546001600160a01b0319908116331782557f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19c830180546001600160a01b0389169216919091179055347f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19d90920191909155610e298642611a43565b600382015560088101610e3c8582611c73565b5060098101610e4b8482611c73565b50600454610e5b90600190611a2a565b9150846001600160a01b0316336001600160a01b0316837fe9097a4f4eddc0e5906640fcd9e1193c9db52771536ca4c8b06ab4c40aa045d234604051610ea391815260200190565b60405180910390a450949350505050565b600060048281548110610ec957610ec96119fe565b60009182526020909120600b9091020190506002600a82015460ff166004811115610ef657610ef6611797565b14610f1457604051638225aba560e01b815260040160405180910390fd5b7f0000000000000000000000000000000000000000000000000000000000000000816007015442610f459190611a2a565b1015610f6457604051634799187b60e01b815260040160405180910390fd5b600681015415610fa557600181015460068201546040516001600160a01b039092169181156108fc0291906000818181858888f15050600060068501555050505b610fb0826001611318565b60016105e3565b600060048281548110610fcc57610fcc6119fe565b90600052602060002090600b020190508060030154421015611001576040516302eb354360e41b815260040160405180910390fd5b6000600a82015460ff16600481111561101c5761101c611797565b1461103a57604051634e22597f60e11b815260040160405180910390fd5b600181015460028201546040516001600160a01b039092169181156108fc0291906000818181858888f15050600060028501819055600a8501805460ff1916600417905592506105e3915050565b60006004838154811061109d5761109d6119fe565b60009182526020909120600b9091020180549091506001600160a01b031633146110da5760405163e2bc376b60e01b815260040160405180910390fd5b6000600a82015460ff1660048111156110f5576110f5611797565b1461111357604051634e22597f60e11b815260040160405180910390fd5b8060020154821115611138576040516305aafecf60e01b815260040160405180910390fd5b60018101546040516001600160a01b039091169083156108fc029084906000818181858888f1935050505050818160020160008282546111789190611a2a565b90915550506040805183815233602082015284917fd1432ca9a38d944f01b256a411861b109bc4bfe200c40d7144e919a16b86a8a8910160405180910390a2505050565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031633146112055760405163c383977560e01b815260040160405180910390fd5b600080546001600160a01b0319166001600160a01b0392909216919091179055565b60006004838154811061123c5761123c6119fe565b600091825260209091206001600b90920201908101549091506001600160a01b0316331461127d57604051635800797f60e11b815260040160405180910390fd5b6000600a82015460ff16600481111561129857611298611797565b146112b657604051634e22597f60e11b815260040160405180910390fd5b80600201548211156112db576040516305aafecf60e01b815260040160405180910390fd5b80546040516001600160a01b039091169083156108fc029084906000818181858888f1935050505050818160020160008282546111789190611a2a565b60006004838154811061132d5761132d6119fe565b60009182526020909120600b90910201905060018203611389578054600282015460058301546001600160a01b03909216916108fc9161136c91611a43565b6040518115909202916000818181858888f1935050505050611431565b600282036113b9576001810154600282015460068301546001600160a01b03909216916108fc9161136c91611a43565b60006002826002015483600501546113d19190611a43565b6113db9190611d2d565b82546040519192506001600160a01b03169082156108fc029083906000818181858888f150505060018401546040516001600160a01b03909116925083156108fc02915083906000818181858888f15050505050505b6000600282018190556005820181905560068201819055600a8201805460ff1916600417905560405160039185917f89e6168584e1de3ec704ff46376271b5481b0d9b2b0ef0ae102f0d001093e0009190a3505050565b60006004838154811061149d5761149d6119fe565b600091825260208220600a600b90920201908101805460ff19166003179055905460405163c13517e160e01b81529192506001600160a01b03169063c13517e19084906114f290600290600190600401611d4f565b60206040518083038185885af1158015611510573d6000803e3d6000fd5b50505050506040513d601f19601f820116820180604052508101906115359190611ae6565b600482018181556000918252600560205260408083208690559054915460035491516001600160a01b03909116917f8bd32f430ff060e6bd204709b3790c9807987263d3230c580dc80b5f89e27186916115a691888252602082015260606040820181905260009082015260800190565b60405180910390a381816005015411156115fe5760008282600501546115cc9190611a2a565b6005830184905582546040519192506001600160a01b03169082156108fc029083906000818181858888f15050505050505b81816006015411156107bb57600082826006015461161c9190611a2a565b6006830184905560018301546040519192506001600160a01b03169082156108fc029083906000818181858888f1505050505050505050565b6000815180845260005b8181101561167b5760208185018101518683018201520161165f565b506000602082860101526020601f19601f83011685010191505092915050565b6020815260006116ae6020830184611655565b9392505050565b6000602082840312156116c757600080fd5b5035919050565b600080604083850312156116e157600080fd5b50508035926020909101359150565b6000806020838503121561170357600080fd5b823567ffffffffffffffff8082111561171b57600080fd5b818501915085601f83011261172f57600080fd5b81358181111561173e57600080fd5b86602082850101111561175057600080fd5b60209290920196919550909350505050565b6001600160a01b038116811461177757600080fd5b50565b60006020828403121561178c57600080fd5b81356116ae81611762565b634e487b7160e01b600052602160045260246000fd5b600061016060018060a01b03808f168452808e166020850152508b60408401528a60608401528960808401528860a08401528760c08401528660e0840152806101008401526117fe81840187611655565b90508281036101208401526118138186611655565b9150506005831061182657611826611797565b826101408301529c9b505050505050505050505050565b634e487b7160e01b600052604160045260246000fd5b600082601f83011261186457600080fd5b813567ffffffffffffffff8082111561187f5761187f61183d565b604051601f8301601f19908116603f011681019082821181831017156118a7576118a761183d565b816040528381528660208588010111156118c057600080fd5b836020870160208301376000602085830101528094505050505092915050565b600080604083850312156118f357600080fd5b823567ffffffffffffffff8082111561190b57600080fd5b61191786838701611853565b9350602085013591508082111561192d57600080fd5b5061193a85828601611853565b9150509250929050565b6000806000806080858703121561195a57600080fd5b84359350602085013561196c81611762565b9250604085013567ffffffffffffffff8082111561198957600080fd5b61199588838901611853565b935060608701359150808211156119ab57600080fd5b506119b887828801611853565b91505092959194509250565b600181811c908216806119d857607f821691505b6020821081036119f857634e487b7160e01b600052602260045260246000fd5b50919050565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b81810381811115611a3d57611a3d611a14565b92915050565b80820180821115611a3d57611a3d611a14565b60008154611a63816119c4565b808552602060018381168015611a805760018114611a9a57611ac8565b60ff1985168884015283151560051b880183019550611ac8565b866000528260002060005b85811015611ac05781548a8201860152908301908401611aa5565b890184019650505b505050505092915050565b6020815260006116ae6020830184611a56565b600060208284031215611af857600080fd5b5051919050565b6020810160038310611b1357611b13611797565b91905290565b601f8211156107bb57600081815260208120601f850160051c81016020861015611b405750805b601f850160051c820191505b81811015611b5f57828155600101611b4c565b505050505050565b600019600383901b1c191660019190911b1790565b67ffffffffffffffff831115611b9457611b9461183d565b611ba883611ba283546119c4565b83611b19565b6000601f841160018114611bd65760008515611bc45750838201355b611bce8682611b67565b845550611c30565b600083815260209020601f19861690835b82811015611c075786850135825560209485019460019092019101611be7565b5086821015611c245760001960f88860031b161c19848701351681555b505060018560011b0183555b5050505050565b6060815260006060820152608060208201526000611c586080830185611655565b8281036040840152611c6a8185611655565b95945050505050565b815167ffffffffffffffff811115611c8d57611c8d61183d565b611ca181611c9b84546119c4565b84611b19565b602080601f831160018114611cd05760008415611cbe5750858301515b611cc88582611b67565b865550611b5f565b600085815260208120601f198616915b82811015611cff57888601518255948401946001909101908401611ce0565b5085821015611d1d5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b600082611d4a57634e487b7160e01b600052601260045260246000fd5b500490565b828152604060208201526000611d686040830184611a56565b94935050505056fea264697066735822122005b0699f8f6e4a5a2b7331292d34d72ef79a72fae2d7ed4c1c12fe8da221be2b64736f6c63430008120033", + "numDeployments": 2, + "solcInputHash": "897897c4b490e62b694ee95b2c0dce7f", + "metadata": "{\"compiler\":{\"version\":\"0.8.18+commit.87f61d96\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[{\"internalType\":\"contract IArbitratorV2\",\"name\":\"_arbitrator\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"_arbitratorExtraData\",\"type\":\"bytes\"},{\"internalType\":\"string\",\"name\":\"_templateData\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"_templateDataMappings\",\"type\":\"string\"},{\"internalType\":\"contract IDisputeTemplateRegistry\",\"name\":\"_templateRegistry\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_feeTimeout\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"ArbitratorOnly\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"BuyerFeeNotCoverArbitrationCosts\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"BuyerOnly\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"DeadlineNotPassed\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"DisputeAlreadyCreatedOrTransactionAlreadyExecuted\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"DisputeAlreadyResolved\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"GovernorOnly\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidRuling\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"MaximumPaymentAmountExceeded\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NotWaitingForBuyerFees\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NotWaitingForSellerFees\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"SellerFeeNotCoverArbitrationCosts\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"SellerOnly\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"TimeoutNotPassed\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"TransactionDisputed\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"contract IArbitratorV2\",\"name\":\"_arbitrator\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"_arbitrableDisputeID\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_externalDisputeID\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_templateId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"_templateUri\",\"type\":\"string\"}],\"name\":\"DisputeRequest\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"_transactionID\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"enum Escrow.Party\",\"name\":\"_party\",\"type\":\"uint8\"}],\"name\":\"HasToPayFee\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"_transactionID\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"_party\",\"type\":\"address\"}],\"name\":\"Payment\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"contract IArbitratorV2\",\"name\":\"_arbitrator\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"_disputeID\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_ruling\",\"type\":\"uint256\"}],\"name\":\"Ruling\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"_transactionID\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"_transactionUri\",\"type\":\"string\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"_buyer\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"_seller\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"_asset\",\"type\":\"string\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_deadline\",\"type\":\"uint256\"}],\"name\":\"TransactionCreated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"_transactionID\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"enum Escrow.Resolution\",\"name\":\"_resolution\",\"type\":\"uint8\"}],\"name\":\"TransactionResolved\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"AMOUNT_OF_CHOICES\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"arbitrator\",\"outputs\":[{\"internalType\":\"contract IArbitratorV2\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"arbitratorExtraData\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IArbitratorV2\",\"name\":\"_arbitrator\",\"type\":\"address\"}],\"name\":\"changeArbitrator\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"_arbitratorExtraData\",\"type\":\"bytes\"}],\"name\":\"changeArbitratorExtraData\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"_templateData\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"_templateDataMappings\",\"type\":\"string\"}],\"name\":\"changeDisputeTemplate\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IDisputeTemplateRegistry\",\"name\":\"_templateRegistry\",\"type\":\"address\"}],\"name\":\"changeTemplateRegistry\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_timeoutPayment\",\"type\":\"uint256\"},{\"internalType\":\"string\",\"name\":\"_transactionUri\",\"type\":\"string\"},{\"internalType\":\"address payable\",\"name\":\"_seller\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"_templateData\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"_templateDataMappings\",\"type\":\"string\"}],\"name\":\"createTransaction\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"transactionID\",\"type\":\"uint256\"}],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"disputeIDtoTransactionID\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_transactionID\",\"type\":\"uint256\"}],\"name\":\"executeTransaction\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"feeTimeout\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getCountTransactions\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"governor\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_transactionID\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"}],\"name\":\"pay\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_transactionID\",\"type\":\"uint256\"}],\"name\":\"payArbitrationFeeByBuyer\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_transactionID\",\"type\":\"uint256\"}],\"name\":\"payArbitrationFeeBySeller\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_transactionID\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_amountReimbursed\",\"type\":\"uint256\"}],\"name\":\"reimburse\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_disputeID\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_ruling\",\"type\":\"uint256\"}],\"name\":\"rule\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"templateId\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"templateRegistry\",\"outputs\":[{\"internalType\":\"contract IDisputeTemplateRegistry\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_transactionID\",\"type\":\"uint256\"}],\"name\":\"timeOutByBuyer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_transactionID\",\"type\":\"uint256\"}],\"name\":\"timeOutBySeller\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"transactions\",\"outputs\":[{\"internalType\":\"address payable\",\"name\":\"buyer\",\"type\":\"address\"},{\"internalType\":\"address payable\",\"name\":\"seller\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"deadline\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"disputeID\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"buyerFee\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"sellerFee\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"lastFeePaymentTime\",\"type\":\"uint256\"},{\"internalType\":\"string\",\"name\":\"templateData\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"templateDataMappings\",\"type\":\"string\"},{\"internalType\":\"enum Escrow.Status\",\"name\":\"status\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"}],\"devdoc\":{\"details\":\"MultipleArbitrableTransaction contract that is compatible with V2. Adapted from https://github.com/kleros/kleros-interaction/blob/master/contracts/standard/arbitration/MultipleArbitrableTransaction.sol\",\"events\":{\"DisputeRequest(address,uint256,uint256,uint256,string)\":{\"details\":\"To be emitted when a dispute is created to link the correct meta-evidence to the disputeID.\",\"params\":{\"_arbitrableDisputeID\":\"The identifier of the dispute in the Arbitrable contract.\",\"_arbitrator\":\"The arbitrator of the contract.\",\"_externalDisputeID\":\"An identifier created outside Kleros by the protocol requesting arbitration.\",\"_templateId\":\"The identifier of the dispute template. Should not be used with _templateUri.\",\"_templateUri\":\"The URI to the dispute template. For example on IPFS: starting with '/ipfs/'. Should not be used with _templateId.\"}},\"HasToPayFee(uint256,uint8)\":{\"details\":\"Indicate that a party has to pay a fee or would otherwise be considered as losing.\",\"params\":{\"_party\":\"The party who has to pay.\",\"_transactionID\":\"The index of the transaction.\"}},\"Payment(uint256,uint256,address)\":{\"details\":\"To be emitted when a party pays or reimburses the other.\",\"params\":{\"_amount\":\"The amount paid.\",\"_party\":\"The party that paid.\",\"_transactionID\":\"The index of the transaction.\"}},\"Ruling(address,uint256,uint256)\":{\"details\":\"To be raised when a ruling is given.\",\"params\":{\"_arbitrator\":\"The arbitrator giving the ruling.\",\"_disputeID\":\"The identifier of the dispute in the Arbitrator contract.\",\"_ruling\":\"The ruling which was given.\"}},\"TransactionCreated(uint256,string,address,address,uint256,string,uint256)\":{\"details\":\"Emitted when a transaction is created.\",\"params\":{\"_amount\":\"The initial amount in the transaction.\",\"_asset\":\"The asset used (\\\"native\\\" for native chain token).\",\"_buyer\":\"The address of the buyer.\",\"_deadline\":\"The deadline of the transaction.\",\"_seller\":\"The address of the seller.\",\"_transactionID\":\"The index of the transaction.\",\"_transactionUri\":\"The IPFS Uri Hash of the transaction.\"}},\"TransactionResolved(uint256,uint8)\":{\"details\":\"To be emitted when a transaction is resolved, either by its execution, a timeout or because a ruling was enforced.\",\"params\":{\"_resolution\":\"Short description of what caused the transaction to be solved.\",\"_transactionID\":\"The ID of the respective transaction.\"}}},\"kind\":\"dev\",\"methods\":{\"constructor\":{\"details\":\"Constructor.\",\"params\":{\"_arbitrator\":\"The arbitrator of the contract.\",\"_arbitratorExtraData\":\"Extra data for the arbitrator.\",\"_feeTimeout\":\"Arbitration fee timeout for the parties.\",\"_templateData\":\"The dispute template data.\",\"_templateDataMappings\":\"The dispute template data mappings.\",\"_templateRegistry\":\"The dispute template registry.\"}},\"createTransaction(uint256,string,address,string,string)\":{\"details\":\"Create a transaction.\",\"params\":{\"_seller\":\"The recipient of the transaction.\",\"_templateData\":\"The dispute template data.\",\"_templateDataMappings\":\"The dispute template data mappings.\",\"_timeoutPayment\":\"Time after which a party can automatically execute the arbitrable transaction.\",\"_transactionUri\":\"The IPFS Uri Hash of the transaction.\"},\"returns\":{\"transactionID\":\"The index of the transaction.\"}},\"executeTransaction(uint256)\":{\"details\":\"Transfer the transaction's amount to the seller if the timeout has passed.\",\"params\":{\"_transactionID\":\"The index of the transaction.\"}},\"getCountTransactions()\":{\"details\":\"Getter to know the count of transactions.\",\"returns\":{\"_0\":\"The count of transactions.\"}},\"pay(uint256,uint256)\":{\"details\":\"Pay seller. To be called if the good or service is provided.\",\"params\":{\"_amount\":\"Amount to pay in wei.\",\"_transactionID\":\"The index of the transaction.\"}},\"payArbitrationFeeByBuyer(uint256)\":{\"details\":\"Pay the arbitration fee to raise a dispute. To be called by the buyer. Note that the arbitrator can have createDispute throw, which will make this function throw and therefore lead to a party being timed-out. This is not a vulnerability as the arbitrator can rule in favor of one party anyway.\",\"params\":{\"_transactionID\":\"The index of the transaction.\"}},\"payArbitrationFeeBySeller(uint256)\":{\"details\":\"Pay the arbitration fee to raise a dispute. To be called by the seller. Note that this function mirrors payArbitrationFeeByBuyer.\",\"params\":{\"_transactionID\":\"The index of the transaction.\"}},\"reimburse(uint256,uint256)\":{\"details\":\"Reimburse buyer. To be called if the good or service can't be fully provided.\",\"params\":{\"_amountReimbursed\":\"Amount to reimburse in wei.\",\"_transactionID\":\"The index of the transaction.\"}},\"rule(uint256,uint256)\":{\"details\":\"Give a ruling for a dispute. Must be called by the arbitrator to enforce the final ruling. The purpose of this function is to ensure that the address calling it has the right to rule on the contract.\",\"params\":{\"_disputeID\":\"ID of the dispute in the Arbitrator contract.\",\"_ruling\":\"Ruling given by the arbitrator. Note that 0 is reserved for \\\"Refuse to arbitrate\\\".\"}},\"timeOutByBuyer(uint256)\":{\"details\":\"Reimburse buyer if seller fails to pay the fee.\",\"params\":{\"_transactionID\":\"The index of the transaction.\"}},\"timeOutBySeller(uint256)\":{\"details\":\"Pay seller if buyer fails to pay the fee.\",\"params\":{\"_transactionID\":\"The index of the transaction.\"}}},\"title\":\"Escrow for a sale paid in ETH and no fees.\",\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"src/arbitration/arbitrables/Escrow.sol\":\"Escrow\"},\"evmVersion\":\"paris\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\",\"useLiteralContent\":true},\"optimizer\":{\"enabled\":true,\"runs\":100},\"remappings\":[]},\"sources\":{\"@openzeppelin/contracts/token/ERC20/IERC20.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev Interface of the ERC20 standard as defined in the EIP.\\n */\\ninterface IERC20 {\\n /**\\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\\n * another (`to`).\\n *\\n * Note that `value` may be zero.\\n */\\n event Transfer(address indexed from, address indexed to, uint256 value);\\n\\n /**\\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\\n * a call to {approve}. `value` is the new allowance.\\n */\\n event Approval(address indexed owner, address indexed spender, uint256 value);\\n\\n /**\\n * @dev Returns the amount of tokens in existence.\\n */\\n function totalSupply() external view returns (uint256);\\n\\n /**\\n * @dev Returns the amount of tokens owned by `account`.\\n */\\n function balanceOf(address account) external view returns (uint256);\\n\\n /**\\n * @dev Moves `amount` tokens from the caller's account to `to`.\\n *\\n * Returns a boolean value indicating whether the operation succeeded.\\n *\\n * Emits a {Transfer} event.\\n */\\n function transfer(address to, uint256 amount) external returns (bool);\\n\\n /**\\n * @dev Returns the remaining number of tokens that `spender` will be\\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\\n * zero by default.\\n *\\n * This value changes when {approve} or {transferFrom} are called.\\n */\\n function allowance(address owner, address spender) external view returns (uint256);\\n\\n /**\\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\\n *\\n * Returns a boolean value indicating whether the operation succeeded.\\n *\\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\\n * that someone may use both the old and the new allowance by unfortunate\\n * transaction ordering. One possible solution to mitigate this race\\n * condition is to first reduce the spender's allowance to 0 and set the\\n * desired value afterwards:\\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\\n *\\n * Emits an {Approval} event.\\n */\\n function approve(address spender, uint256 amount) external returns (bool);\\n\\n /**\\n * @dev Moves `amount` tokens from `from` to `to` using the\\n * allowance mechanism. `amount` is then deducted from the caller's\\n * allowance.\\n *\\n * Returns a boolean value indicating whether the operation succeeded.\\n *\\n * Emits a {Transfer} event.\\n */\\n function transferFrom(address from, address to, uint256 amount) external returns (bool);\\n}\\n\",\"keccak256\":\"0x287b55befed2961a7eabd7d7b1b2839cbca8a5b80ef8dcbb25ed3d4c2002c305\",\"license\":\"MIT\"},\"src/arbitration/arbitrables/Escrow.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n\\n/// @authors: [@unknownunknown1, @fnanni-0, @shalzz, @jaybuidl]\\n/// @reviewers: []\\n/// @auditors: []\\n/// @bounties: []\\n\\npragma solidity 0.8.18;\\n\\nimport {IArbitrableV2, IArbitratorV2} from \\\"../interfaces/IArbitrableV2.sol\\\";\\nimport \\\"../interfaces/IDisputeTemplateRegistry.sol\\\";\\n\\n/// @title Escrow for a sale paid in ETH and no fees.\\n/// @dev MultipleArbitrableTransaction contract that is compatible with V2.\\n/// Adapted from https://github.com/kleros/kleros-interaction/blob/master/contracts/standard/arbitration/MultipleArbitrableTransaction.sol\\ncontract Escrow is IArbitrableV2 {\\n // ************************************* //\\n // * Enums / Structs * //\\n // ************************************* //\\n\\n enum Party {\\n None,\\n Buyer, // Makes a purchase in ETH.\\n Seller // Provides a good or service in exchange for ETH.\\n }\\n\\n enum Status {\\n NoDispute,\\n WaitingBuyer,\\n WaitingSeller,\\n DisputeCreated,\\n TransactionResolved\\n }\\n\\n enum Resolution {\\n TransactionExecuted,\\n TimeoutByBuyer,\\n TimeoutBySeller,\\n RulingEnforced\\n }\\n\\n struct Transaction {\\n address payable buyer;\\n address payable seller;\\n uint256 amount;\\n uint256 deadline; // Timestamp at which the transaction can be automatically executed if not disputed.\\n uint256 disputeID; // If dispute exists, the ID of the dispute.\\n uint256 buyerFee; // Total fees paid by the buyer.\\n uint256 sellerFee; // Total fees paid by the seller.\\n uint256 lastFeePaymentTime; // Last time the dispute fees were paid by either party.\\n string templateData;\\n string templateDataMappings;\\n Status status;\\n }\\n\\n // ************************************* //\\n // * Storage * //\\n // ************************************* //\\n\\n uint256 public constant AMOUNT_OF_CHOICES = 2;\\n address public immutable governor;\\n IArbitratorV2 public arbitrator; // Address of the arbitrator contract.\\n bytes public arbitratorExtraData; // Extra data to set up the arbitration.\\n IDisputeTemplateRegistry public templateRegistry; // The dispute template registry.\\n uint256 public templateId; // The current dispute template identifier.\\n uint256 public immutable feeTimeout; // Time in seconds a party can take to pay arbitration fees before being considered unresponsive and lose the dispute.\\n Transaction[] public transactions; // List of all created transactions.\\n mapping(uint256 => uint256) public disputeIDtoTransactionID; // Naps dispute ID to tx ID.\\n\\n // ************************************* //\\n // * Events * //\\n // ************************************* //\\n\\n /// @dev To be emitted when a party pays or reimburses the other.\\n /// @param _transactionID The index of the transaction.\\n /// @param _amount The amount paid.\\n /// @param _party The party that paid.\\n event Payment(uint256 indexed _transactionID, uint256 _amount, address _party);\\n\\n /// @dev Indicate that a party has to pay a fee or would otherwise be considered as losing.\\n /// @param _transactionID The index of the transaction.\\n /// @param _party The party who has to pay.\\n event HasToPayFee(uint256 indexed _transactionID, Party _party);\\n\\n /// @dev Emitted when a transaction is created.\\n /// @param _transactionID The index of the transaction.\\n /// @param _transactionUri The IPFS Uri Hash of the transaction.\\n /// @param _buyer The address of the buyer.\\n /// @param _seller The address of the seller.\\n /// @param _amount The initial amount in the transaction.\\n /// @param _asset The asset used (\\\"native\\\" for native chain token).\\n /// @param _deadline The deadline of the transaction.\\n event TransactionCreated(\\n uint256 indexed _transactionID,\\n string _transactionUri,\\n address indexed _buyer,\\n address indexed _seller,\\n uint256 _amount,\\n string _asset,\\n uint256 _deadline\\n );\\n\\n /// @dev To be emitted when a transaction is resolved, either by its\\n /// execution, a timeout or because a ruling was enforced.\\n /// @param _transactionID The ID of the respective transaction.\\n /// @param _resolution Short description of what caused the transaction to be solved.\\n event TransactionResolved(uint256 indexed _transactionID, Resolution indexed _resolution);\\n\\n // ************************************* //\\n // * Function Modifiers * //\\n // ************************************* //\\n\\n modifier onlyByGovernor() {\\n if (governor != msg.sender) revert GovernorOnly();\\n _;\\n }\\n\\n // ************************************* //\\n // * Constructor * //\\n // ************************************* //\\n\\n /// @dev Constructor.\\n /// @param _arbitrator The arbitrator of the contract.\\n /// @param _arbitratorExtraData Extra data for the arbitrator.\\n /// @param _templateData The dispute template data.\\n /// @param _templateDataMappings The dispute template data mappings.\\n /// @param _templateRegistry The dispute template registry.\\n /// @param _feeTimeout Arbitration fee timeout for the parties.\\n constructor(\\n IArbitratorV2 _arbitrator,\\n bytes memory _arbitratorExtraData,\\n string memory _templateData,\\n string memory _templateDataMappings,\\n IDisputeTemplateRegistry _templateRegistry,\\n uint256 _feeTimeout\\n ) {\\n governor = msg.sender;\\n arbitrator = _arbitrator;\\n arbitratorExtraData = _arbitratorExtraData;\\n templateRegistry = _templateRegistry;\\n feeTimeout = _feeTimeout;\\n\\n templateId = templateRegistry.setDisputeTemplate(\\\"\\\", _templateData, _templateDataMappings);\\n }\\n\\n // ************************************* //\\n // * Governance * //\\n // ************************************* //\\n\\n function changeArbitrator(IArbitratorV2 _arbitrator) external onlyByGovernor {\\n arbitrator = _arbitrator;\\n }\\n\\n function changeArbitratorExtraData(bytes calldata _arbitratorExtraData) external onlyByGovernor {\\n arbitratorExtraData = _arbitratorExtraData;\\n }\\n\\n function changeTemplateRegistry(IDisputeTemplateRegistry _templateRegistry) external onlyByGovernor {\\n templateRegistry = _templateRegistry;\\n }\\n\\n function changeDisputeTemplate(\\n string memory _templateData,\\n string memory _templateDataMappings\\n ) external onlyByGovernor {\\n templateId = templateRegistry.setDisputeTemplate(\\\"\\\", _templateData, _templateDataMappings);\\n }\\n\\n // ************************************* //\\n // * State Modifiers * //\\n // ************************************* //\\n\\n /// @dev Create a transaction.\\n /// @param _timeoutPayment Time after which a party can automatically execute the arbitrable transaction.\\n /// @param _transactionUri The IPFS Uri Hash of the transaction.\\n /// @param _seller The recipient of the transaction.\\n /// @param _templateData The dispute template data.\\n /// @param _templateDataMappings The dispute template data mappings.\\n /// @return transactionID The index of the transaction.\\n function createTransaction(\\n uint256 _timeoutPayment,\\n string memory _transactionUri,\\n address payable _seller,\\n string memory _templateData,\\n string memory _templateDataMappings\\n ) external payable returns (uint256 transactionID) {\\n Transaction storage transaction = transactions.push();\\n transaction.buyer = payable(msg.sender);\\n transaction.seller = _seller;\\n transaction.amount = msg.value;\\n transaction.deadline = block.timestamp + _timeoutPayment;\\n transaction.templateData = _templateData;\\n transaction.templateDataMappings = _templateDataMappings;\\n\\n transactionID = transactions.length - 1;\\n\\n emit TransactionCreated(\\n transactionID,\\n _transactionUri,\\n msg.sender,\\n _seller,\\n msg.value,\\n \\\"native\\\",\\n transaction.deadline\\n );\\n }\\n\\n /// @dev Pay seller. To be called if the good or service is provided.\\n /// @param _transactionID The index of the transaction.\\n /// @param _amount Amount to pay in wei.\\n function pay(uint256 _transactionID, uint256 _amount) external {\\n Transaction storage transaction = transactions[_transactionID];\\n if (transaction.buyer != msg.sender) revert BuyerOnly();\\n if (transaction.status != Status.NoDispute) revert TransactionDisputed();\\n if (_amount > transaction.amount) revert MaximumPaymentAmountExceeded();\\n\\n transaction.seller.send(_amount); // It is the user responsibility to accept ETH.\\n transaction.amount -= _amount;\\n\\n emit Payment(_transactionID, _amount, msg.sender);\\n }\\n\\n /// @dev Reimburse buyer. To be called if the good or service can't be fully provided.\\n /// @param _transactionID The index of the transaction.\\n /// @param _amountReimbursed Amount to reimburse in wei.\\n function reimburse(uint256 _transactionID, uint256 _amountReimbursed) external {\\n Transaction storage transaction = transactions[_transactionID];\\n if (transaction.seller != msg.sender) revert SellerOnly();\\n if (transaction.status != Status.NoDispute) revert TransactionDisputed();\\n if (_amountReimbursed > transaction.amount) revert MaximumPaymentAmountExceeded();\\n\\n transaction.buyer.send(_amountReimbursed); // It is the user responsibility to accept ETH.\\n transaction.amount -= _amountReimbursed;\\n\\n emit Payment(_transactionID, _amountReimbursed, msg.sender);\\n }\\n\\n /// @dev Transfer the transaction's amount to the seller if the timeout has passed.\\n /// @param _transactionID The index of the transaction.\\n function executeTransaction(uint256 _transactionID) external {\\n Transaction storage transaction = transactions[_transactionID];\\n if (block.timestamp < transaction.deadline) revert DeadlineNotPassed();\\n if (transaction.status != Status.NoDispute) revert TransactionDisputed();\\n\\n transaction.seller.send(transaction.amount); // It is the user responsibility to accept ETH.\\n transaction.amount = 0;\\n transaction.status = Status.TransactionResolved;\\n\\n emit TransactionResolved(_transactionID, Resolution.TransactionExecuted);\\n }\\n\\n /// @dev Pay the arbitration fee to raise a dispute. To be called by the buyer.\\n /// Note that the arbitrator can have createDispute throw, which will make\\n /// this function throw and therefore lead to a party being timed-out.\\n /// This is not a vulnerability as the arbitrator can rule in favor of one party anyway.\\n /// @param _transactionID The index of the transaction.\\n function payArbitrationFeeByBuyer(uint256 _transactionID) external payable {\\n Transaction storage transaction = transactions[_transactionID];\\n if (transaction.status >= Status.DisputeCreated) revert DisputeAlreadyCreatedOrTransactionAlreadyExecuted();\\n if (msg.sender != transaction.buyer) revert BuyerOnly();\\n\\n transaction.buyerFee += msg.value;\\n uint256 arbitrationCost = arbitrator.arbitrationCost(arbitratorExtraData);\\n if (transaction.buyerFee < arbitrationCost) revert BuyerFeeNotCoverArbitrationCosts();\\n\\n transaction.lastFeePaymentTime = block.timestamp;\\n\\n if (transaction.sellerFee < arbitrationCost) {\\n // The seller still has to pay. This can also happen if he has paid, but arbitrationCost has increased.\\n transaction.status = Status.WaitingSeller;\\n emit HasToPayFee(_transactionID, Party.Seller);\\n } else {\\n // The seller has also paid the fee. We create the dispute.\\n raiseDispute(_transactionID, arbitrationCost);\\n }\\n }\\n\\n /// @dev Pay the arbitration fee to raise a dispute. To be called by the seller.\\n /// Note that this function mirrors payArbitrationFeeByBuyer.\\n /// @param _transactionID The index of the transaction.\\n function payArbitrationFeeBySeller(uint256 _transactionID) external payable {\\n Transaction storage transaction = transactions[_transactionID];\\n if (transaction.status >= Status.DisputeCreated) revert DisputeAlreadyCreatedOrTransactionAlreadyExecuted();\\n if (msg.sender != transaction.seller) revert SellerOnly();\\n\\n transaction.sellerFee += msg.value;\\n uint256 arbitrationCost = arbitrator.arbitrationCost(arbitratorExtraData);\\n if (transaction.sellerFee < arbitrationCost) revert SellerFeeNotCoverArbitrationCosts();\\n\\n transaction.lastFeePaymentTime = block.timestamp;\\n\\n if (transaction.buyerFee < arbitrationCost) {\\n // The buyer still has to pay. This can also happen if he has paid, but arbitrationCost has increased.\\n transaction.status = Status.WaitingBuyer;\\n emit HasToPayFee(_transactionID, Party.Buyer);\\n } else {\\n // The buyer has also paid the fee. We create the dispute.\\n raiseDispute(_transactionID, arbitrationCost);\\n }\\n }\\n\\n /// @dev Reimburse buyer if seller fails to pay the fee.\\n /// @param _transactionID The index of the transaction.\\n function timeOutByBuyer(uint256 _transactionID) external {\\n Transaction storage transaction = transactions[_transactionID];\\n if (transaction.status != Status.WaitingSeller) revert NotWaitingForSellerFees();\\n if (block.timestamp - transaction.lastFeePaymentTime < feeTimeout) revert TimeoutNotPassed();\\n\\n if (transaction.sellerFee != 0) {\\n transaction.seller.send(transaction.sellerFee); // It is the user responsibility to accept ETH.\\n transaction.sellerFee = 0;\\n }\\n executeRuling(_transactionID, uint256(Party.Buyer));\\n emit TransactionResolved(_transactionID, Resolution.TimeoutByBuyer);\\n }\\n\\n /// @dev Pay seller if buyer fails to pay the fee.\\n /// @param _transactionID The index of the transaction.\\n function timeOutBySeller(uint256 _transactionID) external {\\n Transaction storage transaction = transactions[_transactionID];\\n if (transaction.status != Status.WaitingBuyer) revert NotWaitingForBuyerFees();\\n if (block.timestamp - transaction.lastFeePaymentTime < feeTimeout) revert TimeoutNotPassed();\\n\\n if (transaction.buyerFee != 0) {\\n transaction.buyer.send(transaction.buyerFee); // It is the user responsibility to accept ETH.\\n transaction.buyerFee = 0;\\n }\\n\\n executeRuling(_transactionID, uint256(Party.Seller));\\n emit TransactionResolved(_transactionID, Resolution.TimeoutBySeller);\\n }\\n\\n /// @dev Give a ruling for a dispute. Must be called by the arbitrator to enforce the final ruling.\\n /// The purpose of this function is to ensure that the address calling it has the right to rule on the contract.\\n /// @param _disputeID ID of the dispute in the Arbitrator contract.\\n /// @param _ruling Ruling given by the arbitrator. Note that 0 is reserved\\n /// for \\\"Refuse to arbitrate\\\".\\n function rule(uint256 _disputeID, uint256 _ruling) external override {\\n if (msg.sender != address(arbitrator)) revert ArbitratorOnly();\\n if (_ruling > AMOUNT_OF_CHOICES) revert InvalidRuling();\\n\\n uint256 transactionID = disputeIDtoTransactionID[_disputeID];\\n Transaction storage transaction = transactions[transactionID];\\n if (transaction.status != Status.DisputeCreated) revert DisputeAlreadyResolved();\\n\\n emit Ruling(arbitrator, _disputeID, _ruling);\\n executeRuling(transactionID, _ruling);\\n }\\n\\n // ************************************* //\\n // * Internal * //\\n // ************************************* //\\n\\n /// @dev Create a dispute.\\n /// @param _transactionID The index of the transaction.\\n /// @param _arbitrationCost Amount to pay the arbitrator.\\n function raiseDispute(uint256 _transactionID, uint256 _arbitrationCost) internal {\\n Transaction storage transaction = transactions[_transactionID];\\n transaction.status = Status.DisputeCreated;\\n transaction.disputeID = arbitrator.createDispute{value: _arbitrationCost}(\\n AMOUNT_OF_CHOICES,\\n arbitratorExtraData\\n );\\n disputeIDtoTransactionID[transaction.disputeID] = _transactionID;\\n emit DisputeRequest(arbitrator, transaction.disputeID, _transactionID, templateId, \\\"\\\");\\n\\n // Refund buyer if he overpaid.\\n if (transaction.buyerFee > _arbitrationCost) {\\n uint256 extraFeeBuyer = transaction.buyerFee - _arbitrationCost;\\n transaction.buyerFee = _arbitrationCost;\\n transaction.buyer.send(extraFeeBuyer); // It is the user responsibility to accept ETH.\\n }\\n\\n // Refund seller if he overpaid.\\n if (transaction.sellerFee > _arbitrationCost) {\\n uint256 extraFeeSeller = transaction.sellerFee - _arbitrationCost;\\n transaction.sellerFee = _arbitrationCost;\\n transaction.seller.send(extraFeeSeller); // It is the user responsibility to accept ETH.\\n }\\n }\\n\\n /// @dev Execute a ruling of a dispute. It reimburses the fee to the winning party.\\n /// @param _transactionID The index of the transaction.\\n /// @param _ruling Ruling given by the arbitrator. 1 : Reimburse the seller. 2 : Pay the buyer.\\n function executeRuling(uint256 _transactionID, uint256 _ruling) internal {\\n Transaction storage transaction = transactions[_transactionID];\\n // Give the arbitration fee back.\\n // Note that we use send to prevent a party from blocking the execution.\\n if (_ruling == uint256(Party.Buyer)) {\\n transaction.buyer.send(transaction.buyerFee + transaction.amount);\\n } else if (_ruling == uint256(Party.Seller)) {\\n transaction.seller.send(transaction.sellerFee + transaction.amount);\\n } else {\\n uint256 splitAmount = (transaction.buyerFee + transaction.amount) / 2;\\n transaction.buyer.send(splitAmount);\\n transaction.seller.send(splitAmount);\\n }\\n\\n transaction.amount = 0;\\n transaction.buyerFee = 0;\\n transaction.sellerFee = 0;\\n transaction.status = Status.TransactionResolved;\\n\\n emit TransactionResolved(_transactionID, Resolution.RulingEnforced);\\n }\\n\\n // ************************************* //\\n // * Public Views * //\\n // ************************************* //\\n\\n /// @dev Getter to know the count of transactions.\\n /// @return The count of transactions.\\n function getCountTransactions() external view returns (uint256) {\\n return transactions.length;\\n }\\n\\n // ************************************* //\\n // * Errors * //\\n // ************************************* //\\n\\n error GovernorOnly();\\n error BuyerOnly();\\n error SellerOnly();\\n error ArbitratorOnly();\\n error TransactionDisputed();\\n error MaximumPaymentAmountExceeded();\\n error DisputeAlreadyCreatedOrTransactionAlreadyExecuted();\\n error DeadlineNotPassed();\\n error BuyerFeeNotCoverArbitrationCosts();\\n error SellerFeeNotCoverArbitrationCosts();\\n error NotWaitingForSellerFees();\\n error NotWaitingForBuyerFees();\\n error TimeoutNotPassed();\\n error InvalidRuling();\\n error DisputeAlreadyResolved();\\n}\\n\",\"keccak256\":\"0xedd0780518b7604eaf8710df3d23dddf07a0cdc54b9a90a9c55d15d30e381842\",\"license\":\"MIT\"},\"src/arbitration/interfaces/IArbitrableV2.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n\\npragma solidity 0.8.18;\\n\\nimport \\\"./IArbitratorV2.sol\\\";\\n\\n/// @title IArbitrableV2\\n/// @notice Arbitrable interface.\\n/// When developing arbitrable contracts, we need to:\\n/// - Define the action taken when a ruling is received by the contract.\\n/// - Allow dispute creation. For this a function must call arbitrator.createDispute{value: _fee}(_choices,_extraData);\\ninterface IArbitrableV2 {\\n /// @dev To be emitted when a dispute is created to link the correct meta-evidence to the disputeID.\\n /// @param _arbitrator The arbitrator of the contract.\\n /// @param _arbitrableDisputeID The identifier of the dispute in the Arbitrable contract.\\n /// @param _externalDisputeID An identifier created outside Kleros by the protocol requesting arbitration.\\n /// @param _templateId The identifier of the dispute template. Should not be used with _templateUri.\\n /// @param _templateUri The URI to the dispute template. For example on IPFS: starting with '/ipfs/'. Should not be used with _templateId.\\n event DisputeRequest(\\n IArbitratorV2 indexed _arbitrator,\\n uint256 indexed _arbitrableDisputeID,\\n uint256 _externalDisputeID,\\n uint256 _templateId,\\n string _templateUri\\n );\\n\\n /// @dev To be raised when a ruling is given.\\n /// @param _arbitrator The arbitrator giving the ruling.\\n /// @param _disputeID The identifier of the dispute in the Arbitrator contract.\\n /// @param _ruling The ruling which was given.\\n event Ruling(IArbitratorV2 indexed _arbitrator, uint256 indexed _disputeID, uint256 _ruling);\\n\\n /// @dev Give a ruling for a dispute.\\n /// Must be called by the arbitrator.\\n /// The purpose of this function is to ensure that the address calling it has the right to rule on the contract.\\n /// @param _disputeID The identifier of the dispute in the Arbitrator contract.\\n /// @param _ruling Ruling given by the arbitrator.\\n /// Note that 0 is reserved for \\\"Not able/wanting to make a decision\\\".\\n function rule(uint256 _disputeID, uint256 _ruling) external;\\n}\\n\",\"keccak256\":\"0x389326b1f749454ed179bdac2f9d6ce24a1ef944bbce976ca78b93f4e173354a\",\"license\":\"MIT\"},\"src/arbitration/interfaces/IArbitratorV2.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n\\npragma solidity 0.8.18;\\n\\nimport \\\"@openzeppelin/contracts/token/ERC20/IERC20.sol\\\";\\nimport \\\"./IArbitrableV2.sol\\\";\\n\\n/// @title Arbitrator\\n/// Arbitrator interface that implements the new arbitration standard.\\n/// Unlike the ERC-792 this standard is not concerned with appeals, so each arbitrator can implement an appeal system that suits it the most.\\n/// When developing arbitrator contracts we need to:\\n/// - Define the functions for dispute creation (createDispute). Don't forget to store the arbitrated contract and the disputeID (which should be unique, may nbDisputes).\\n/// - Define the functions for cost display (arbitrationCost).\\n/// - Allow giving rulings. For this a function must call arbitrable.rule(disputeID, ruling).\\ninterface IArbitratorV2 {\\n /// @dev To be emitted when a dispute is created.\\n /// @param _disputeID The identifier of the dispute in the Arbitrator contract.\\n /// @param _arbitrable The contract which created the dispute.\\n event DisputeCreation(uint256 indexed _disputeID, IArbitrableV2 indexed _arbitrable);\\n\\n /// @dev To be raised when a ruling is given.\\n /// @param _arbitrable The arbitrable receiving the ruling.\\n /// @param _disputeID The identifier of the dispute in the Arbitrator contract.\\n /// @param _ruling The ruling which was given.\\n event Ruling(IArbitrableV2 indexed _arbitrable, uint256 indexed _disputeID, uint256 _ruling);\\n\\n /// @dev To be emitted when an ERC20 token is added or removed as a method to pay fees.\\n /// @param _token The ERC20 token.\\n /// @param _accepted Whether the token is accepted or not.\\n event AcceptedFeeToken(IERC20 indexed _token, bool indexed _accepted);\\n\\n /// @dev To be emitted when the fee for a particular ERC20 token is updated.\\n /// @param _feeToken The ERC20 token.\\n /// @param _rateInEth The new rate of the fee token in ETH.\\n /// @param _rateDecimals The new decimals of the fee token rate.\\n event NewCurrencyRate(IERC20 indexed _feeToken, uint64 _rateInEth, uint8 _rateDecimals);\\n\\n /// @dev Create a dispute and pay for the fees in the native currency, typically ETH.\\n /// Must be called by the arbitrable contract.\\n /// Must pay at least arbitrationCost(_extraData).\\n /// @param _numberOfChoices The number of choices the arbitrator can choose from in this dispute.\\n /// @param _extraData Additional info about the dispute. We use it to pass the ID of the dispute's court (first 32 bytes), the minimum number of jurors required (next 32 bytes) and the ID of the specific dispute kit (last 32 bytes).\\n /// @return disputeID The identifier of the dispute created.\\n function createDispute(\\n uint256 _numberOfChoices,\\n bytes calldata _extraData\\n ) external payable returns (uint256 disputeID);\\n\\n /// @dev Create a dispute and pay for the fees in a supported ERC20 token.\\n /// Must be called by the arbitrable contract.\\n /// Must pay at least arbitrationCost(_extraData).\\n /// @param _numberOfChoices The number of choices the arbitrator can choose from in this dispute.\\n /// @param _extraData Additional info about the dispute. We use it to pass the ID of the dispute's court (first 32 bytes), the minimum number of jurors required (next 32 bytes) and the ID of the specific dispute kit (last 32 bytes).\\n /// @param _feeToken The ERC20 token used to pay fees.\\n /// @param _feeAmount Amount of the ERC20 token used to pay fees.\\n /// @return disputeID The identifier of the dispute created.\\n function createDispute(\\n uint256 _numberOfChoices,\\n bytes calldata _extraData,\\n IERC20 _feeToken,\\n uint256 _feeAmount\\n ) external returns (uint256 disputeID);\\n\\n /// @dev Compute the cost of arbitration denominated in the native currency, typically ETH.\\n /// It is recommended not to increase it often, as it can be highly time and gas consuming for the arbitrated contracts to cope with fee augmentation.\\n /// @param _extraData Additional info about the dispute. We use it to pass the ID of the dispute's court (first 32 bytes), the minimum number of jurors required (next 32 bytes) and the ID of the specific dispute kit (last 32 bytes).\\n /// @return cost The arbitration cost in ETH.\\n function arbitrationCost(bytes calldata _extraData) external view returns (uint256 cost);\\n\\n /// @dev Compute the cost of arbitration denominated in `_feeToken`.\\n /// It is recommended not to increase it often, as it can be highly time and gas consuming for the arbitrated contracts to cope with fee augmentation.\\n /// @param _extraData Additional info about the dispute. We use it to pass the ID of the dispute's court (first 32 bytes), the minimum number of jurors required (next 32 bytes) and the ID of the specific dispute kit (last 32 bytes).\\n /// @param _feeToken The ERC20 token used to pay fees.\\n /// @return cost The arbitration cost in `_feeToken`.\\n function arbitrationCost(bytes calldata _extraData, IERC20 _feeToken) external view returns (uint256 cost);\\n\\n /// @dev Gets the current ruling of a specified dispute.\\n /// @param _disputeID The ID of the dispute.\\n /// @return ruling The current ruling.\\n /// @return tied Whether it's a tie or not.\\n /// @return overridden Whether the ruling was overridden by appeal funding or not.\\n function currentRuling(uint256 _disputeID) external view returns (uint256 ruling, bool tied, bool overridden);\\n}\\n\",\"keccak256\":\"0x453943ba5ccc94b9b9cdfd4afd3678682d62d8b90fe16b43e90215387d2f6a51\",\"license\":\"MIT\"},\"src/arbitration/interfaces/IDisputeTemplateRegistry.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n\\npragma solidity 0.8.18;\\n\\n/// @title IDisputeTemplate\\n/// @notice Dispute Template interface.\\ninterface IDisputeTemplateRegistry {\\n /// @dev To be emitted when a new dispute template is created.\\n /// @param _templateId The identifier of the dispute template.\\n /// @param _templateTag An optional tag for the dispute template, such as \\\"registration\\\" or \\\"removal\\\".\\n /// @param _templateData The template data.\\n /// @param _templateDataMappings The data mappings.\\n event DisputeTemplate(\\n uint256 indexed _templateId,\\n string indexed _templateTag,\\n string _templateData,\\n string _templateDataMappings\\n );\\n\\n function setDisputeTemplate(\\n string memory _templateTag,\\n string memory _templateData,\\n string memory _templateDataMappings\\n ) external returns (uint256 templateId);\\n}\\n\",\"keccak256\":\"0x88b0038d226532e6cf862a485d162f7bca61ac3d361d6801146b55a240f091ac\",\"license\":\"MIT\"}},\"version\":1}", + "bytecode": "0x60c06040523480156200001157600080fd5b50604051620023373803806200233783398101604081905262000034916200020d565b33608052600080546001600160a01b0319166001600160a01b038816179055600162000061868262000376565b50600280546001600160a01b0319166001600160a01b03841690811790915560a08290526040516312a6505d60e21b8152634a99417490620000aa908790879060040162000470565b6020604051808303816000875af1158015620000ca573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620000f09190620004b0565b60035550620004ca945050505050565b6001600160a01b03811681146200011657600080fd5b50565b634e487b7160e01b600052604160045260246000fd5b60005b838110156200014c57818101518382015260200162000132565b50506000910152565b60006001600160401b038084111562000172576200017262000119565b604051601f8501601f19908116603f011681019082821181831017156200019d576200019d62000119565b81604052809350858152868686011115620001b757600080fd5b620001c78660208301876200012f565b5050509392505050565b600082601f830112620001e357600080fd5b620001f48383516020850162000155565b9392505050565b8051620002088162000100565b919050565b60008060008060008060c087890312156200022757600080fd5b8651620002348162000100565b60208801519096506001600160401b03808211156200025257600080fd5b818901915089601f8301126200026757600080fd5b620002788a83516020850162000155565b965060408901519150808211156200028f57600080fd5b6200029d8a838b01620001d1565b95506060890151915080821115620002b457600080fd5b50620002c389828a01620001d1565b935050620002d460808801620001fb565b915060a087015190509295509295509295565b600181811c90821680620002fc57607f821691505b6020821081036200031d57634e487b7160e01b600052602260045260246000fd5b50919050565b601f8211156200037157600081815260208120601f850160051c810160208610156200034c5750805b601f850160051c820191505b818110156200036d5782815560010162000358565b5050505b505050565b81516001600160401b0381111562000392576200039262000119565b620003aa81620003a38454620002e7565b8462000323565b602080601f831160018114620003e25760008415620003c95750858301515b600019600386901b1c1916600185901b1785556200036d565b600085815260208120601f198616915b828110156200041357888601518255948401946001909101908401620003f2565b5085821015620004325787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b600081518084526200045c8160208601602086016200012f565b601f01601f19169290920160200192915050565b606081526000606082015260806020820152600062000493608083018562000442565b8281036040840152620004a7818562000442565b95945050505050565b600060208284031215620004c357600080fd5b5051919050565b60805160a051611e1d6200051a60003960008181610377015281816105490152610f1d015260008181610157015281816108d201528181610abd01528181610df801526111c50152611e1d6000f3fe6080604052600436106101405760003560e01c80636cc6cde1116100b6578063c5d552881161006f578063c5d5528814610399578063e77d0bd3146103b9578063ee22610b146103d9578063ef48eee6146103f9578063fc548f0814610419578063fe43a9921461043957600080fd5b80636cc6cde1146102c35780637aa77f29146102e35780639ace38c2146102f9578063a0af81f014610330578063a527aa6a14610350578063b329036b1461036557600080fd5b80632fbe3b03116101085780632fbe3b0314610210578063311a6c561461023d57806334e2672d1461025d5780633bf547241461027d5780634660ebbe146102905780634ba5845b146102b057600080fd5b80630c340a24146101455780630c7ac7b6146101965780631bd1823a146101b85780632be6d005146101da5780632e0b6422146101ed575b600080fd5b34801561015157600080fd5b506101797f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020015b60405180910390f35b3480156101a257600080fd5b506101ab610459565b60405161018d91906116a2565b3480156101c457600080fd5b506101d86101d33660046116bc565b6104e7565b005b6101d86101e83660046116bc565b610612565b3480156101f957600080fd5b50610202600281565b60405190815260200161018d565b34801561021c57600080fd5b5061020261022b3660046116bc565b60056020526000908152604090205481565b34801561024957600080fd5b506101d86102583660046116d5565b6107c0565b34801561026957600080fd5b506101d86102783660046116f7565b6108d0565b6101d861028b3660046116bc565b610926565b34801561029c57600080fd5b506101d86102ab366004611781565b610abb565b6102026102be366004611841565b610b26565b3480156102cf57600080fd5b50600054610179906001600160a01b031681565b3480156102ef57600080fd5b5061020260035481565b34801561030557600080fd5b506103196103143660046116bc565b610c69565b60405161018d9b9a999897969594939291906118fe565b34801561033c57600080fd5b50600254610179906001600160a01b031681565b34801561035c57600080fd5b50600454610202565b34801561037157600080fd5b506102027f000000000000000000000000000000000000000000000000000000000000000081565b3480156103a557600080fd5b506101d86103b436600461198e565b610df6565b3480156103c557600080fd5b506101d86103d43660046116bc565b610ebb565b3480156103e557600080fd5b506101d86103f43660046116bc565b610fbe565b34801561040557600080fd5b506101d86104143660046116d5565b61108f565b34801561042557600080fd5b506101d8610434366004611781565b6111c3565b34801561044557600080fd5b506101d86104543660046116d5565b61122e565b60018054610466906119f2565b80601f0160208091040260200160405190810160405280929190818152602001828054610492906119f2565b80156104df5780601f106104b4576101008083540402835291602001916104df565b820191906000526020600020905b8154815290600101906020018083116104c257829003601f168201915b505050505081565b6000600482815481106104fc576104fc611a2c565b60009182526020909120600b9091020190506001600a82015460ff166004811115610529576105296118e8565b1461054757604051635ed7670b60e11b815260040160405180910390fd5b7f00000000000000000000000000000000000000000000000000000000000000008160070154426105789190611a58565b101561059757604051634799187b60e01b815260040160405180910390fd5b6005810154156105d557805460058201546040516001600160a01b039092169181156108fc0291906000818181858888f15050600060058501555050505b6105e082600261131f565b60025b60405183907f89e6168584e1de3ec704ff46376271b5481b0d9b2b0ef0ae102f0d001093e00090600090a35050565b60006004828154811061062757610627611a2c565b60009182526020909120600b9091020190506003600a82015460ff166004811115610654576106546118e8565b106106725760405163df44e9d960e01b815260040160405180910390fd5b80546001600160a01b0316331461069c5760405163e2bc376b60e01b815260040160405180910390fd5b348160050160008282546106b09190611a71565b90915550506000805460405163f7434ea960e01b81526001600160a01b039091169063f7434ea9906106e790600190600401611b01565b602060405180830381865afa158015610704573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107289190611b14565b9050808260050154101561074f57604051631a48f3db60e11b815260040160405180910390fd5b42600783015560068201548111156107b157600a8201805460ff1916600290811790915560405184917fc74b9f7dedf2887cd3c113b6d8da9cea19e55c1116e25f1f0e1b72d7543179b5916107a49190611b2d565b60405180910390a2505050565b6107bb838261148f565b505050565b6000546001600160a01b031633146107eb57604051630955f84760e31b815260040160405180910390fd5b600281111561080d576040516309efd47960e41b815260040160405180910390fd5b600082815260056020526040812054600480549192918390811061083357610833611a2c565b60009182526020909120600b9091020190506003600a82015460ff166004811115610860576108606118e8565b1461087e5760405163f10068b560e01b815260040160405180910390fd5b60005460405184815285916001600160a01b0316907f394027a5fa6e098a1191094d1719d6929b9abc535fcc0c8f448d6a4e756222769060200160405180910390a36108ca828461131f565b50505050565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031633146109195760405163c383977560e01b815260040160405180910390fd5b60016107bb828483611baa565b60006004828154811061093b5761093b611a2c565b60009182526020909120600b9091020190506003600a82015460ff166004811115610968576109686118e8565b106109865760405163df44e9d960e01b815260040160405180910390fd5b60018101546001600160a01b031633146109b357604051635800797f60e11b815260040160405180910390fd5b348160060160008282546109c79190611a71565b90915550506000805460405163f7434ea960e01b81526001600160a01b039091169063f7434ea9906109fe90600190600401611b01565b602060405180830381865afa158015610a1b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a3f9190611b14565b90508082600601541015610a665760405163818d938f60e01b815260040160405180910390fd5b42600783015560058201548111156107b157600a8201805460ff1916600190811790915560405184917fc74b9f7dedf2887cd3c113b6d8da9cea19e55c1116e25f1f0e1b72d7543179b5916107a49190611b2d565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03163314610b045760405163c383977560e01b815260040160405180910390fd5b600280546001600160a01b0319166001600160a01b0392909216919091179055565b600480546001810182556000918252600b027f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b810180546001600160a01b0319908116331782557f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19c830180546001600160a01b0389169216919091179055347f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19d90920191909155610bd78742611a71565b600382015560088101610bea8582611c65565b5060098101610bf98482611c65565b50600454610c0990600190611a58565b9150846001600160a01b0316336001600160a01b0316837facce36df44ab52b164cd7cbe013bca2f8a44e5a2cfe4753f5480f79a3116230989348660030154604051610c5793929190611d1f565b60405180910390a45095945050505050565b60048181548110610c7957600080fd5b60009182526020909120600b90910201805460018201546002830154600384015460048501546005860154600687015460078801546008890180546001600160a01b03998a169b50989097169895979496939592949193909291610cdc906119f2565b80601f0160208091040260200160405190810160405280929190818152602001828054610d08906119f2565b8015610d555780601f10610d2a57610100808354040283529160200191610d55565b820191906000526020600020905b815481529060010190602001808311610d3857829003601f168201915b505050505090806009018054610d6a906119f2565b80601f0160208091040260200160405190810160405280929190818152602001828054610d96906119f2565b8015610de35780601f10610db857610100808354040283529160200191610de3565b820191906000526020600020905b815481529060010190602001808311610dc657829003601f168201915b505050600a909301549192505060ff168b565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03163314610e3f5760405163c383977560e01b815260040160405180910390fd5b6002546040516312a6505d60e21b81526001600160a01b0390911690634a99417490610e719085908590600401611d68565b6020604051808303816000875af1158015610e90573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610eb49190611b14565b6003555050565b600060048281548110610ed057610ed0611a2c565b60009182526020909120600b9091020190506002600a82015460ff166004811115610efd57610efd6118e8565b14610f1b57604051638225aba560e01b815260040160405180910390fd5b7f0000000000000000000000000000000000000000000000000000000000000000816007015442610f4c9190611a58565b1015610f6b57604051634799187b60e01b815260040160405180910390fd5b600681015415610fac57600181015460068201546040516001600160a01b039092169181156108fc0291906000818181858888f15050600060068501555050505b610fb782600161131f565b60016105e3565b600060048281548110610fd357610fd3611a2c565b90600052602060002090600b020190508060030154421015611008576040516302eb354360e41b815260040160405180910390fd5b6000600a82015460ff166004811115611023576110236118e8565b1461104157604051634e22597f60e11b815260040160405180910390fd5b600181015460028201546040516001600160a01b039092169181156108fc0291906000818181858888f15050600060028501819055600a8501805460ff1916600417905592506105e3915050565b6000600483815481106110a4576110a4611a2c565b60009182526020909120600b9091020180549091506001600160a01b031633146110e15760405163e2bc376b60e01b815260040160405180910390fd5b6000600a82015460ff1660048111156110fc576110fc6118e8565b1461111a57604051634e22597f60e11b815260040160405180910390fd5b806002015482111561113f576040516305aafecf60e01b815260040160405180910390fd5b60018101546040516001600160a01b039091169083156108fc029084906000818181858888f19350505050508181600201600082825461117f9190611a58565b90915550506040805183815233602082015284917fd1432ca9a38d944f01b256a411861b109bc4bfe200c40d7144e919a16b86a8a8910160405180910390a2505050565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316331461120c5760405163c383977560e01b815260040160405180910390fd5b600080546001600160a01b0319166001600160a01b0392909216919091179055565b60006004838154811061124357611243611a2c565b600091825260209091206001600b90920201908101549091506001600160a01b0316331461128457604051635800797f60e11b815260040160405180910390fd5b6000600a82015460ff16600481111561129f5761129f6118e8565b146112bd57604051634e22597f60e11b815260040160405180910390fd5b80600201548211156112e2576040516305aafecf60e01b815260040160405180910390fd5b80546040516001600160a01b039091169083156108fc029084906000818181858888f19350505050508181600201600082825461117f9190611a58565b60006004838154811061133457611334611a2c565b60009182526020909120600b90910201905060018203611390578054600282015460058301546001600160a01b03909216916108fc9161137391611a71565b6040518115909202916000818181858888f1935050505050611438565b600282036113c0576001810154600282015460068301546001600160a01b03909216916108fc9161137391611a71565b60006002826002015483600501546113d89190611a71565b6113e29190611da4565b82546040519192506001600160a01b03169082156108fc029083906000818181858888f150505060018401546040516001600160a01b03909116925083156108fc02915083906000818181858888f15050505050505b6000600282018190556005820181905560068201819055600a8201805460ff1916600417905560405160039185917f89e6168584e1de3ec704ff46376271b5481b0d9b2b0ef0ae102f0d001093e0009190a3505050565b6000600483815481106114a4576114a4611a2c565b600091825260208220600a600b90920201908101805460ff19166003179055905460405163c13517e160e01b81529192506001600160a01b03169063c13517e19084906114f990600290600190600401611dc6565b60206040518083038185885af1158015611517573d6000803e3d6000fd5b50505050506040513d601f19601f8201168201806040525081019061153c9190611b14565b600482018181556000918252600560205260408083208690559054915460035491516001600160a01b03909116917f8bd32f430ff060e6bd204709b3790c9807987263d3230c580dc80b5f89e27186916115ad91888252602082015260606040820181905260009082015260800190565b60405180910390a381816005015411156116055760008282600501546115d39190611a58565b6005830184905582546040519192506001600160a01b03169082156108fc029083906000818181858888f15050505050505b81816006015411156107bb5760008282600601546116239190611a58565b6006830184905560018301546040519192506001600160a01b03169082156108fc029083906000818181858888f1505050505050505050565b6000815180845260005b8181101561168257602081850181015186830182015201611666565b506000602082860101526020601f19601f83011685010191505092915050565b6020815260006116b5602083018461165c565b9392505050565b6000602082840312156116ce57600080fd5b5035919050565b600080604083850312156116e857600080fd5b50508035926020909101359150565b6000806020838503121561170a57600080fd5b823567ffffffffffffffff8082111561172257600080fd5b818501915085601f83011261173657600080fd5b81358181111561174557600080fd5b86602082850101111561175757600080fd5b60209290920196919550909350505050565b6001600160a01b038116811461177e57600080fd5b50565b60006020828403121561179357600080fd5b81356116b581611769565b634e487b7160e01b600052604160045260246000fd5b600082601f8301126117c557600080fd5b813567ffffffffffffffff808211156117e0576117e061179e565b604051601f8301601f19908116603f011681019082821181831017156118085761180861179e565b8160405283815286602085880101111561182157600080fd5b836020870160208301376000602085830101528094505050505092915050565b600080600080600060a0868803121561185957600080fd5b85359450602086013567ffffffffffffffff8082111561187857600080fd5b61188489838a016117b4565b95506040880135915061189682611769565b909350606087013590808211156118ac57600080fd5b6118b889838a016117b4565b935060808801359150808211156118ce57600080fd5b506118db888289016117b4565b9150509295509295909350565b634e487b7160e01b600052602160045260246000fd5b600061016060018060a01b03808f168452808e166020850152508b60408401528a60608401528960808401528860a08401528760c08401528660e08401528061010084015261194f8184018761165c565b9050828103610120840152611964818661165c565b91505060058310611977576119776118e8565b826101408301529c9b505050505050505050505050565b600080604083850312156119a157600080fd5b823567ffffffffffffffff808211156119b957600080fd5b6119c5868387016117b4565b935060208501359150808211156119db57600080fd5b506119e8858286016117b4565b9150509250929050565b600181811c90821680611a0657607f821691505b602082108103611a2657634e487b7160e01b600052602260045260246000fd5b50919050565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b81810381811115611a6b57611a6b611a42565b92915050565b80820180821115611a6b57611a6b611a42565b60008154611a91816119f2565b808552602060018381168015611aae5760018114611ac857611af6565b60ff1985168884015283151560051b880183019550611af6565b866000528260002060005b85811015611aee5781548a8201860152908301908401611ad3565b890184019650505b505050505092915050565b6020815260006116b56020830184611a84565b600060208284031215611b2657600080fd5b5051919050565b6020810160038310611b4157611b416118e8565b91905290565b601f8211156107bb57600081815260208120601f850160051c81016020861015611b6e5750805b601f850160051c820191505b81811015611b8d57828155600101611b7a565b505050505050565b600019600383901b1c191660019190911b1790565b67ffffffffffffffff831115611bc257611bc261179e565b611bd683611bd083546119f2565b83611b47565b6000601f841160018114611c045760008515611bf25750838201355b611bfc8682611b95565b845550611c5e565b600083815260209020601f19861690835b82811015611c355786850135825560209485019460019092019101611c15565b5086821015611c525760001960f88860031b161c19848701351681555b505060018560011b0183555b5050505050565b815167ffffffffffffffff811115611c7f57611c7f61179e565b611c9381611c8d84546119f2565b84611b47565b602080601f831160018114611cc25760008415611cb05750858301515b611cba8582611b95565b865550611b8d565b600085815260208120601f198616915b82811015611cf157888601518255948401946001909101908401611cd2565b5085821015611d0f5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b608081526000611d32608083018661165c565b846020840152828103604084015260068152656e617469766560d01b602082015260408101915050826060830152949350505050565b6060815260006060820152608060208201526000611d89608083018561165c565b8281036040840152611d9b818561165c565b95945050505050565b600082611dc157634e487b7160e01b600052601260045260246000fd5b500490565b828152604060208201526000611ddf6040830184611a84565b94935050505056fea26469706673582212207b8a69e6b2aa0219311c7eedf4074e3de313c888959d68e7f6807d8fc0d856d164736f6c63430008120033", + "deployedBytecode": "0x6080604052600436106101405760003560e01c80636cc6cde1116100b6578063c5d552881161006f578063c5d5528814610399578063e77d0bd3146103b9578063ee22610b146103d9578063ef48eee6146103f9578063fc548f0814610419578063fe43a9921461043957600080fd5b80636cc6cde1146102c35780637aa77f29146102e35780639ace38c2146102f9578063a0af81f014610330578063a527aa6a14610350578063b329036b1461036557600080fd5b80632fbe3b03116101085780632fbe3b0314610210578063311a6c561461023d57806334e2672d1461025d5780633bf547241461027d5780634660ebbe146102905780634ba5845b146102b057600080fd5b80630c340a24146101455780630c7ac7b6146101965780631bd1823a146101b85780632be6d005146101da5780632e0b6422146101ed575b600080fd5b34801561015157600080fd5b506101797f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020015b60405180910390f35b3480156101a257600080fd5b506101ab610459565b60405161018d91906116a2565b3480156101c457600080fd5b506101d86101d33660046116bc565b6104e7565b005b6101d86101e83660046116bc565b610612565b3480156101f957600080fd5b50610202600281565b60405190815260200161018d565b34801561021c57600080fd5b5061020261022b3660046116bc565b60056020526000908152604090205481565b34801561024957600080fd5b506101d86102583660046116d5565b6107c0565b34801561026957600080fd5b506101d86102783660046116f7565b6108d0565b6101d861028b3660046116bc565b610926565b34801561029c57600080fd5b506101d86102ab366004611781565b610abb565b6102026102be366004611841565b610b26565b3480156102cf57600080fd5b50600054610179906001600160a01b031681565b3480156102ef57600080fd5b5061020260035481565b34801561030557600080fd5b506103196103143660046116bc565b610c69565b60405161018d9b9a999897969594939291906118fe565b34801561033c57600080fd5b50600254610179906001600160a01b031681565b34801561035c57600080fd5b50600454610202565b34801561037157600080fd5b506102027f000000000000000000000000000000000000000000000000000000000000000081565b3480156103a557600080fd5b506101d86103b436600461198e565b610df6565b3480156103c557600080fd5b506101d86103d43660046116bc565b610ebb565b3480156103e557600080fd5b506101d86103f43660046116bc565b610fbe565b34801561040557600080fd5b506101d86104143660046116d5565b61108f565b34801561042557600080fd5b506101d8610434366004611781565b6111c3565b34801561044557600080fd5b506101d86104543660046116d5565b61122e565b60018054610466906119f2565b80601f0160208091040260200160405190810160405280929190818152602001828054610492906119f2565b80156104df5780601f106104b4576101008083540402835291602001916104df565b820191906000526020600020905b8154815290600101906020018083116104c257829003601f168201915b505050505081565b6000600482815481106104fc576104fc611a2c565b60009182526020909120600b9091020190506001600a82015460ff166004811115610529576105296118e8565b1461054757604051635ed7670b60e11b815260040160405180910390fd5b7f00000000000000000000000000000000000000000000000000000000000000008160070154426105789190611a58565b101561059757604051634799187b60e01b815260040160405180910390fd5b6005810154156105d557805460058201546040516001600160a01b039092169181156108fc0291906000818181858888f15050600060058501555050505b6105e082600261131f565b60025b60405183907f89e6168584e1de3ec704ff46376271b5481b0d9b2b0ef0ae102f0d001093e00090600090a35050565b60006004828154811061062757610627611a2c565b60009182526020909120600b9091020190506003600a82015460ff166004811115610654576106546118e8565b106106725760405163df44e9d960e01b815260040160405180910390fd5b80546001600160a01b0316331461069c5760405163e2bc376b60e01b815260040160405180910390fd5b348160050160008282546106b09190611a71565b90915550506000805460405163f7434ea960e01b81526001600160a01b039091169063f7434ea9906106e790600190600401611b01565b602060405180830381865afa158015610704573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107289190611b14565b9050808260050154101561074f57604051631a48f3db60e11b815260040160405180910390fd5b42600783015560068201548111156107b157600a8201805460ff1916600290811790915560405184917fc74b9f7dedf2887cd3c113b6d8da9cea19e55c1116e25f1f0e1b72d7543179b5916107a49190611b2d565b60405180910390a2505050565b6107bb838261148f565b505050565b6000546001600160a01b031633146107eb57604051630955f84760e31b815260040160405180910390fd5b600281111561080d576040516309efd47960e41b815260040160405180910390fd5b600082815260056020526040812054600480549192918390811061083357610833611a2c565b60009182526020909120600b9091020190506003600a82015460ff166004811115610860576108606118e8565b1461087e5760405163f10068b560e01b815260040160405180910390fd5b60005460405184815285916001600160a01b0316907f394027a5fa6e098a1191094d1719d6929b9abc535fcc0c8f448d6a4e756222769060200160405180910390a36108ca828461131f565b50505050565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031633146109195760405163c383977560e01b815260040160405180910390fd5b60016107bb828483611baa565b60006004828154811061093b5761093b611a2c565b60009182526020909120600b9091020190506003600a82015460ff166004811115610968576109686118e8565b106109865760405163df44e9d960e01b815260040160405180910390fd5b60018101546001600160a01b031633146109b357604051635800797f60e11b815260040160405180910390fd5b348160060160008282546109c79190611a71565b90915550506000805460405163f7434ea960e01b81526001600160a01b039091169063f7434ea9906109fe90600190600401611b01565b602060405180830381865afa158015610a1b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a3f9190611b14565b90508082600601541015610a665760405163818d938f60e01b815260040160405180910390fd5b42600783015560058201548111156107b157600a8201805460ff1916600190811790915560405184917fc74b9f7dedf2887cd3c113b6d8da9cea19e55c1116e25f1f0e1b72d7543179b5916107a49190611b2d565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03163314610b045760405163c383977560e01b815260040160405180910390fd5b600280546001600160a01b0319166001600160a01b0392909216919091179055565b600480546001810182556000918252600b027f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b810180546001600160a01b0319908116331782557f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19c830180546001600160a01b0389169216919091179055347f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19d90920191909155610bd78742611a71565b600382015560088101610bea8582611c65565b5060098101610bf98482611c65565b50600454610c0990600190611a58565b9150846001600160a01b0316336001600160a01b0316837facce36df44ab52b164cd7cbe013bca2f8a44e5a2cfe4753f5480f79a3116230989348660030154604051610c5793929190611d1f565b60405180910390a45095945050505050565b60048181548110610c7957600080fd5b60009182526020909120600b90910201805460018201546002830154600384015460048501546005860154600687015460078801546008890180546001600160a01b03998a169b50989097169895979496939592949193909291610cdc906119f2565b80601f0160208091040260200160405190810160405280929190818152602001828054610d08906119f2565b8015610d555780601f10610d2a57610100808354040283529160200191610d55565b820191906000526020600020905b815481529060010190602001808311610d3857829003601f168201915b505050505090806009018054610d6a906119f2565b80601f0160208091040260200160405190810160405280929190818152602001828054610d96906119f2565b8015610de35780601f10610db857610100808354040283529160200191610de3565b820191906000526020600020905b815481529060010190602001808311610dc657829003601f168201915b505050600a909301549192505060ff168b565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03163314610e3f5760405163c383977560e01b815260040160405180910390fd5b6002546040516312a6505d60e21b81526001600160a01b0390911690634a99417490610e719085908590600401611d68565b6020604051808303816000875af1158015610e90573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610eb49190611b14565b6003555050565b600060048281548110610ed057610ed0611a2c565b60009182526020909120600b9091020190506002600a82015460ff166004811115610efd57610efd6118e8565b14610f1b57604051638225aba560e01b815260040160405180910390fd5b7f0000000000000000000000000000000000000000000000000000000000000000816007015442610f4c9190611a58565b1015610f6b57604051634799187b60e01b815260040160405180910390fd5b600681015415610fac57600181015460068201546040516001600160a01b039092169181156108fc0291906000818181858888f15050600060068501555050505b610fb782600161131f565b60016105e3565b600060048281548110610fd357610fd3611a2c565b90600052602060002090600b020190508060030154421015611008576040516302eb354360e41b815260040160405180910390fd5b6000600a82015460ff166004811115611023576110236118e8565b1461104157604051634e22597f60e11b815260040160405180910390fd5b600181015460028201546040516001600160a01b039092169181156108fc0291906000818181858888f15050600060028501819055600a8501805460ff1916600417905592506105e3915050565b6000600483815481106110a4576110a4611a2c565b60009182526020909120600b9091020180549091506001600160a01b031633146110e15760405163e2bc376b60e01b815260040160405180910390fd5b6000600a82015460ff1660048111156110fc576110fc6118e8565b1461111a57604051634e22597f60e11b815260040160405180910390fd5b806002015482111561113f576040516305aafecf60e01b815260040160405180910390fd5b60018101546040516001600160a01b039091169083156108fc029084906000818181858888f19350505050508181600201600082825461117f9190611a58565b90915550506040805183815233602082015284917fd1432ca9a38d944f01b256a411861b109bc4bfe200c40d7144e919a16b86a8a8910160405180910390a2505050565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316331461120c5760405163c383977560e01b815260040160405180910390fd5b600080546001600160a01b0319166001600160a01b0392909216919091179055565b60006004838154811061124357611243611a2c565b600091825260209091206001600b90920201908101549091506001600160a01b0316331461128457604051635800797f60e11b815260040160405180910390fd5b6000600a82015460ff16600481111561129f5761129f6118e8565b146112bd57604051634e22597f60e11b815260040160405180910390fd5b80600201548211156112e2576040516305aafecf60e01b815260040160405180910390fd5b80546040516001600160a01b039091169083156108fc029084906000818181858888f19350505050508181600201600082825461117f9190611a58565b60006004838154811061133457611334611a2c565b60009182526020909120600b90910201905060018203611390578054600282015460058301546001600160a01b03909216916108fc9161137391611a71565b6040518115909202916000818181858888f1935050505050611438565b600282036113c0576001810154600282015460068301546001600160a01b03909216916108fc9161137391611a71565b60006002826002015483600501546113d89190611a71565b6113e29190611da4565b82546040519192506001600160a01b03169082156108fc029083906000818181858888f150505060018401546040516001600160a01b03909116925083156108fc02915083906000818181858888f15050505050505b6000600282018190556005820181905560068201819055600a8201805460ff1916600417905560405160039185917f89e6168584e1de3ec704ff46376271b5481b0d9b2b0ef0ae102f0d001093e0009190a3505050565b6000600483815481106114a4576114a4611a2c565b600091825260208220600a600b90920201908101805460ff19166003179055905460405163c13517e160e01b81529192506001600160a01b03169063c13517e19084906114f990600290600190600401611dc6565b60206040518083038185885af1158015611517573d6000803e3d6000fd5b50505050506040513d601f19601f8201168201806040525081019061153c9190611b14565b600482018181556000918252600560205260408083208690559054915460035491516001600160a01b03909116917f8bd32f430ff060e6bd204709b3790c9807987263d3230c580dc80b5f89e27186916115ad91888252602082015260606040820181905260009082015260800190565b60405180910390a381816005015411156116055760008282600501546115d39190611a58565b6005830184905582546040519192506001600160a01b03169082156108fc029083906000818181858888f15050505050505b81816006015411156107bb5760008282600601546116239190611a58565b6006830184905560018301546040519192506001600160a01b03169082156108fc029083906000818181858888f1505050505050505050565b6000815180845260005b8181101561168257602081850181015186830182015201611666565b506000602082860101526020601f19601f83011685010191505092915050565b6020815260006116b5602083018461165c565b9392505050565b6000602082840312156116ce57600080fd5b5035919050565b600080604083850312156116e857600080fd5b50508035926020909101359150565b6000806020838503121561170a57600080fd5b823567ffffffffffffffff8082111561172257600080fd5b818501915085601f83011261173657600080fd5b81358181111561174557600080fd5b86602082850101111561175757600080fd5b60209290920196919550909350505050565b6001600160a01b038116811461177e57600080fd5b50565b60006020828403121561179357600080fd5b81356116b581611769565b634e487b7160e01b600052604160045260246000fd5b600082601f8301126117c557600080fd5b813567ffffffffffffffff808211156117e0576117e061179e565b604051601f8301601f19908116603f011681019082821181831017156118085761180861179e565b8160405283815286602085880101111561182157600080fd5b836020870160208301376000602085830101528094505050505092915050565b600080600080600060a0868803121561185957600080fd5b85359450602086013567ffffffffffffffff8082111561187857600080fd5b61188489838a016117b4565b95506040880135915061189682611769565b909350606087013590808211156118ac57600080fd5b6118b889838a016117b4565b935060808801359150808211156118ce57600080fd5b506118db888289016117b4565b9150509295509295909350565b634e487b7160e01b600052602160045260246000fd5b600061016060018060a01b03808f168452808e166020850152508b60408401528a60608401528960808401528860a08401528760c08401528660e08401528061010084015261194f8184018761165c565b9050828103610120840152611964818661165c565b91505060058310611977576119776118e8565b826101408301529c9b505050505050505050505050565b600080604083850312156119a157600080fd5b823567ffffffffffffffff808211156119b957600080fd5b6119c5868387016117b4565b935060208501359150808211156119db57600080fd5b506119e8858286016117b4565b9150509250929050565b600181811c90821680611a0657607f821691505b602082108103611a2657634e487b7160e01b600052602260045260246000fd5b50919050565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b81810381811115611a6b57611a6b611a42565b92915050565b80820180821115611a6b57611a6b611a42565b60008154611a91816119f2565b808552602060018381168015611aae5760018114611ac857611af6565b60ff1985168884015283151560051b880183019550611af6565b866000528260002060005b85811015611aee5781548a8201860152908301908401611ad3565b890184019650505b505050505092915050565b6020815260006116b56020830184611a84565b600060208284031215611b2657600080fd5b5051919050565b6020810160038310611b4157611b416118e8565b91905290565b601f8211156107bb57600081815260208120601f850160051c81016020861015611b6e5750805b601f850160051c820191505b81811015611b8d57828155600101611b7a565b505050505050565b600019600383901b1c191660019190911b1790565b67ffffffffffffffff831115611bc257611bc261179e565b611bd683611bd083546119f2565b83611b47565b6000601f841160018114611c045760008515611bf25750838201355b611bfc8682611b95565b845550611c5e565b600083815260209020601f19861690835b82811015611c355786850135825560209485019460019092019101611c15565b5086821015611c525760001960f88860031b161c19848701351681555b505060018560011b0183555b5050505050565b815167ffffffffffffffff811115611c7f57611c7f61179e565b611c9381611c8d84546119f2565b84611b47565b602080601f831160018114611cc25760008415611cb05750858301515b611cba8582611b95565b865550611b8d565b600085815260208120601f198616915b82811015611cf157888601518255948401946001909101908401611cd2565b5085821015611d0f5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b608081526000611d32608083018661165c565b846020840152828103604084015260068152656e617469766560d01b602082015260408101915050826060830152949350505050565b6060815260006060820152608060208201526000611d89608083018561165c565b8281036040840152611d9b818561165c565b95945050505050565b600082611dc157634e487b7160e01b600052601260045260246000fd5b500490565b828152604060208201526000611ddf6040830184611a84565b94935050505056fea26469706673582212207b8a69e6b2aa0219311c7eedf4074e3de313c888959d68e7f6807d8fc0d856d164736f6c63430008120033", "devdoc": { "details": "MultipleArbitrableTransaction contract that is compatible with V2. Adapted from https://github.com/kleros/kleros-interaction/blob/master/contracts/standard/arbitration/MultipleArbitrableTransaction.sol", "events": { @@ -773,13 +796,16 @@ "_ruling": "The ruling which was given." } }, - "TransactionCreated(uint256,address,address,uint256)": { + "TransactionCreated(uint256,string,address,address,uint256,string,uint256)": { "details": "Emitted when a transaction is created.", "params": { "_amount": "The initial amount in the transaction.", + "_asset": "The asset used (\"native\" for native chain token).", "_buyer": "The address of the buyer.", + "_deadline": "The deadline of the transaction.", "_seller": "The address of the seller.", - "_transactionID": "The index of the transaction." + "_transactionID": "The index of the transaction.", + "_transactionUri": "The IPFS Uri Hash of the transaction." } }, "TransactionResolved(uint256,uint8)": { @@ -803,13 +829,14 @@ "_templateRegistry": "The dispute template registry." } }, - "createTransaction(uint256,address,string,string)": { + "createTransaction(uint256,string,address,string,string)": { "details": "Create a transaction.", "params": { "_seller": "The recipient of the transaction.", "_templateData": "The dispute template data.", "_templateDataMappings": "The dispute template data mappings.", - "_timeoutPayment": "Time after which a party can automatically execute the arbitrable transaction." + "_timeoutPayment": "Time after which a party can automatically execute the arbitrable transaction.", + "_transactionUri": "The IPFS Uri Hash of the transaction." }, "returns": { "transactionID": "The index of the transaction." @@ -884,15 +911,15 @@ "storageLayout": { "storage": [ { - "astId": 10600, + "astId": 924, "contract": "src/arbitration/arbitrables/Escrow.sol:Escrow", "label": "arbitrator", "offset": 0, "slot": "0", - "type": "t_contract(IArbitratorV2)17049" + "type": "t_contract(IArbitratorV2)2146" }, { - "astId": 10602, + "astId": 926, "contract": "src/arbitration/arbitrables/Escrow.sol:Escrow", "label": "arbitratorExtraData", "offset": 0, @@ -900,15 +927,15 @@ "type": "t_bytes_storage" }, { - "astId": 10605, + "astId": 929, "contract": "src/arbitration/arbitrables/Escrow.sol:Escrow", "label": "templateRegistry", "offset": 0, "slot": "2", - "type": "t_contract(IDisputeTemplateRegistry)17216" + "type": "t_contract(IDisputeTemplateRegistry)2172" }, { - "astId": 10607, + "astId": 931, "contract": "src/arbitration/arbitrables/Escrow.sol:Escrow", "label": "templateId", "offset": 0, @@ -916,15 +943,15 @@ "type": "t_uint256" }, { - "astId": 10613, + "astId": 937, "contract": "src/arbitration/arbitrables/Escrow.sol:Escrow", "label": "transactions", "offset": 0, "slot": "4", - "type": "t_array(t_struct(Transaction)10592_storage)dyn_storage" + "type": "t_array(t_struct(Transaction)916_storage)dyn_storage" }, { - "astId": 10617, + "astId": 941, "contract": "src/arbitration/arbitrables/Escrow.sol:Escrow", "label": "disputeIDtoTransactionID", "offset": 0, @@ -938,8 +965,8 @@ "label": "address payable", "numberOfBytes": "20" }, - "t_array(t_struct(Transaction)10592_storage)dyn_storage": { - "base": "t_struct(Transaction)10592_storage", + "t_array(t_struct(Transaction)916_storage)dyn_storage": { + "base": "t_struct(Transaction)916_storage", "encoding": "dynamic_array", "label": "struct Escrow.Transaction[]", "numberOfBytes": "32" @@ -949,17 +976,17 @@ "label": "bytes", "numberOfBytes": "32" }, - "t_contract(IArbitratorV2)17049": { + "t_contract(IArbitratorV2)2146": { "encoding": "inplace", "label": "contract IArbitratorV2", "numberOfBytes": "20" }, - "t_contract(IDisputeTemplateRegistry)17216": { + "t_contract(IDisputeTemplateRegistry)2172": { "encoding": "inplace", "label": "contract IDisputeTemplateRegistry", "numberOfBytes": "20" }, - "t_enum(Status)10563": { + "t_enum(Status)887": { "encoding": "inplace", "label": "enum Escrow.Status", "numberOfBytes": "1" @@ -976,12 +1003,12 @@ "label": "string", "numberOfBytes": "32" }, - "t_struct(Transaction)10592_storage": { + "t_struct(Transaction)916_storage": { "encoding": "inplace", "label": "struct Escrow.Transaction", "members": [ { - "astId": 10570, + "astId": 894, "contract": "src/arbitration/arbitrables/Escrow.sol:Escrow", "label": "buyer", "offset": 0, @@ -989,7 +1016,7 @@ "type": "t_address_payable" }, { - "astId": 10572, + "astId": 896, "contract": "src/arbitration/arbitrables/Escrow.sol:Escrow", "label": "seller", "offset": 0, @@ -997,7 +1024,7 @@ "type": "t_address_payable" }, { - "astId": 10574, + "astId": 898, "contract": "src/arbitration/arbitrables/Escrow.sol:Escrow", "label": "amount", "offset": 0, @@ -1005,7 +1032,7 @@ "type": "t_uint256" }, { - "astId": 10576, + "astId": 900, "contract": "src/arbitration/arbitrables/Escrow.sol:Escrow", "label": "deadline", "offset": 0, @@ -1013,7 +1040,7 @@ "type": "t_uint256" }, { - "astId": 10578, + "astId": 902, "contract": "src/arbitration/arbitrables/Escrow.sol:Escrow", "label": "disputeID", "offset": 0, @@ -1021,7 +1048,7 @@ "type": "t_uint256" }, { - "astId": 10580, + "astId": 904, "contract": "src/arbitration/arbitrables/Escrow.sol:Escrow", "label": "buyerFee", "offset": 0, @@ -1029,7 +1056,7 @@ "type": "t_uint256" }, { - "astId": 10582, + "astId": 906, "contract": "src/arbitration/arbitrables/Escrow.sol:Escrow", "label": "sellerFee", "offset": 0, @@ -1037,7 +1064,7 @@ "type": "t_uint256" }, { - "astId": 10584, + "astId": 908, "contract": "src/arbitration/arbitrables/Escrow.sol:Escrow", "label": "lastFeePaymentTime", "offset": 0, @@ -1045,7 +1072,7 @@ "type": "t_uint256" }, { - "astId": 10586, + "astId": 910, "contract": "src/arbitration/arbitrables/Escrow.sol:Escrow", "label": "templateData", "offset": 0, @@ -1053,7 +1080,7 @@ "type": "t_string_storage" }, { - "astId": 10588, + "astId": 912, "contract": "src/arbitration/arbitrables/Escrow.sol:Escrow", "label": "templateDataMappings", "offset": 0, @@ -1061,12 +1088,12 @@ "type": "t_string_storage" }, { - "astId": 10591, + "astId": 915, "contract": "src/arbitration/arbitrables/Escrow.sol:Escrow", "label": "status", "offset": 0, "slot": "10", - "type": "t_enum(Status)10563" + "type": "t_enum(Status)887" } ], "numberOfBytes": "352" diff --git a/contracts/package.json b/contracts/package.json index 36dc89378..4b09fb6df 100644 --- a/contracts/package.json +++ b/contracts/package.json @@ -36,7 +36,7 @@ "export:devnet": "yarn hardhat export --export deployments/arbitrumSepoliaDevnet.ts --network arbitrumSepoliaDevnet", "export:testnet": "yarn hardhat export --export deployments/arbitrumSepolia.ts --network arbitrumSepolia", "export:mainnet": "yarn hardhat export --export deployments/arbitrum.ts --network arbitrum", - "viem:test": "NODE_NO_WARNINGS=1 NODE_OPTIONS=--experimental-fetch ts-node ./scripts/viem-test.ts", + "viem:test": "NODE_NO_WARNINGS=1 NODE_OPTIONS=--experimental-fetch ts-node ./scripts/viemTest.ts", "bot:keeper": "NODE_NO_WARNINGS=1 NODE_OPTIONS=--experimental-fetch hardhat run ./scripts/keeperBot.ts", "bot:relayer-from-chiado": "NODE_NO_WARNINGS=1 NODE_OPTIONS=--experimental-fetch hardhat run ./scripts/disputeRelayerBotFromChiado.ts", "bot:relayer-from-sepolia": "NODE_NO_WARNINGS=1 NODE_OPTIONS=--experimental-fetch hardhat run ./scripts/disputeRelayerBotFromSepolia.ts", @@ -70,6 +70,7 @@ "@types/mocha": "^10.0.6", "@types/node": "^16.18.68", "@wagmi/cli": "^1.5.2", + "abitype": "^0.10.3", "chai": "^4.3.10", "dotenv": "^16.3.1", "ethereumjs-util": "^7.1.5", diff --git a/contracts/scripts/getCourtsV1.ts b/contracts/scripts/getCourtsV1.ts index 16c387d48..628d404e3 100644 --- a/contracts/scripts/getCourtsV1.ts +++ b/contracts/scripts/getCourtsV1.ts @@ -47,7 +47,7 @@ async function main() { (result) => ({ id: courtId, - parent: result.parent.toString(), + parent: result.parent.toNumber(), hiddenVotes: result.hiddenVotes, minStake: result.minStake.toString(), alpha: result.alpha.toString(), diff --git a/contracts/scripts/populateCourts.ts b/contracts/scripts/populateCourts.ts index 38b8bb114..46d9b7b00 100644 --- a/contracts/scripts/populateCourts.ts +++ b/contracts/scripts/populateCourts.ts @@ -1,6 +1,6 @@ import { deployments, getNamedAccounts, getChainId, ethers, network } from "hardhat"; import { KlerosCore } from "../typechain-types"; -import { BigNumber } from "ethers"; +import { BigNumber, BigNumberish } from "ethers"; import courtsV1Mainnet from "../config/courts.v1.mainnet.json"; import courtsV1GnosisChain from "../config/courts.v1.gnosischain.json"; import courtsV2ArbitrumTestnet from "../config/courts.v2.testnet.json"; @@ -20,8 +20,20 @@ enum Sources { V2_TESTNET, } +type Court = { + id: number; + parent: number; + hiddenVotes: boolean; + minStake: BigNumberish; + alpha: BigNumberish; + feeForJuror: BigNumberish; + jurorsForCourtJump: BigNumberish; + timesPerPeriod: BigNumberish[]; + supportedDisputeKits?: BigNumberish[]; +}; + const from = isDevnet(network) ? Sources.V2_DEVNET : Sources.V2_TESTNET; -const TESTING_PARAMETERS = false; +const V1_DEV_PARAMETERS = false; // rename to V1_DEV_PARAMETERS const ETH_USD = BigNumber.from(1800); const DISPUTE_KIT_CLASSIC = BigNumber.from(1); const TEN_THOUSAND_GWEI = BigNumber.from(10).pow(13); @@ -40,54 +52,50 @@ async function main() { const truncateWei = (x: BigNumber) => x.div(TEN_THOUSAND_GWEI).mul(TEN_THOUSAND_GWEI); - const parametersUsdToEth = (court) => ({ + const parametersUsdToEth = (court: Court): Court => ({ ...court, minStake: truncateWei(BigNumber.from(court.minStake).div(ETH_USD)), feeForJuror: truncateWei(BigNumber.from(court.feeForJuror).div(ETH_USD)), }); - // TODO: rename this to Devnet instead of Testing - const parametersProductionToTesting = (court) => ({ + const parametersProductionToDev = (court: Court): Court => ({ ...court, - hiddenVotes: false, minStake: truncateWei(BigNumber.from(court.minStake).div(10000)), feeForJuror: truncateWei(ethers.utils.parseEther("0.00001")), timesPerPeriod: [120, 120, 120, 240], }); // WARNING: skip the Forking court at id 0, so the v1 courts are shifted by 1 - const parametersV1ToV2 = (court) => ({ + const parametersV1ToV2 = (court: Court): Court => ({ ...court, - id: BigNumber.from(court.id).add(1), - parent: BigNumber.from(court.parent).add(1), + id: court.id++, + parent: court.parent++, }); let courtsV2; switch (+from) { case Sources.V1_MAINNET: { - let courtsV1 = courtsV1Mainnet; - courtsV1 = TESTING_PARAMETERS ? courtsV1.map(parametersProductionToTesting) : courtsV1; + let courtsV1: Court[] = courtsV1Mainnet; + courtsV1 = V1_DEV_PARAMETERS ? courtsV1.map(parametersProductionToDev) : courtsV1; courtsV2 = courtsV1.map(parametersV1ToV2); break; } case Sources.V1_GNOSIS: { - let courtsV1 = courtsV1GnosisChain.map(parametersUsdToEth); - courtsV1 = TESTING_PARAMETERS ? courtsV1.map(parametersProductionToTesting) : courtsV1; + let courtsV1: Court[] = courtsV1GnosisChain.map(parametersUsdToEth); + courtsV1 = V1_DEV_PARAMETERS ? courtsV1.map(parametersProductionToDev) : courtsV1; courtsV2 = courtsV1.map(parametersV1ToV2); break; } case Sources.V2_DEVNET: { - courtsV2 = courtsV2ArbitrumDevnet.map(parametersProductionToTesting); + courtsV2 = courtsV2ArbitrumDevnet.map(parametersProductionToDev); break; } case Sources.V2_TESTNET: { - courtsV2 = TESTING_PARAMETERS - ? courtsV2ArbitrumTestnet.map(parametersProductionToTesting) - : courtsV2ArbitrumTestnet; + courtsV2 = courtsV2ArbitrumTestnet; break; } default: - return; + throw new Error("Unknown source"); } console.log("courtsV2 = %O", courtsV2); diff --git a/contracts/scripts/populateReadme.sh b/contracts/scripts/populateReadme.sh new file mode 100755 index 000000000..30442c1af --- /dev/null +++ b/contracts/scripts/populateReadme.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)" + +if [ ! -x "$(command -v envsubst)" ]; then + echo >&2 "error: envsubst not installed" + exit 1 +fi + +deployments="$($SCRIPT_DIR/generateDeploymentsMarkdown.sh)" \ + envsubst '$deployments' \ + < README.md.template \ + > README.md diff --git a/contracts/scripts/viem-test.ts b/contracts/scripts/viem-test.ts deleted file mode 100644 index b019d3ebb..000000000 --- a/contracts/scripts/viem-test.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { createPublicClient, http, getContract } from "viem"; -import { arbitrumSepolia } from "viem/chains"; -import { disputeKitClassicConfig } from "../deployments/devnet.viem"; - -const main = async () => { - const client = createPublicClient({ - chain: arbitrumSepolia, - transport: http(), - }); - - const disputeKit = getContract({ - address: disputeKitClassicConfig.address[arbitrumSepolia.id], - abi: disputeKitClassicConfig.abi, - publicClient: client, - }); - - await disputeKit.read.governor().then(console.log); -}; - -main() - .then(() => process.exit(0)) - .catch((error) => { - console.error(error); - process.exit(1); - }); diff --git a/contracts/scripts/viemTest.ts b/contracts/scripts/viemTest.ts new file mode 100644 index 000000000..17c19d783 --- /dev/null +++ b/contracts/scripts/viemTest.ts @@ -0,0 +1,53 @@ +import { createPublicClient, http, getContract } from "viem"; +import { arbitrumSepolia } from "viem/chains"; +import { disputeKitClassicConfig } from "../deployments/devnet.viem"; +import { AbiFunction, AbiParametersToPrimitiveTypes, ExtractAbiFunction, FormatAbiItem } from "abitype"; + +const main = async () => { + const client = createPublicClient({ + chain: arbitrumSepolia, + transport: http(), + }); + + const disputeKit = getContract({ + address: disputeKitClassicConfig.address[arbitrumSepolia.id], + abi: disputeKitClassicConfig.abi, + publicClient: client, + }); + + await disputeKit.read.governor().then(console.log); + + // -------------------------------------------------- + + // Working around the "unknown tuple types" issue + // https://viem.sh/docs/faq.html#why-are-contract-function-args-with-fully-named-inputs-represented-as-unnamed-tuple-types-instead-of-object-types + + // Not human-readable + type DelayedStakesFunction = ExtractAbiFunction; + type Result = AbiParametersToPrimitiveTypes; + // -> readonly [bigint, boolean, `0x${string}`] + // Ideally we would get an object instead of a tuple + + // Human-readable + type FormattedFunction = FormatAbiItem; + // -> "function disputes(uint256) view returns (uint256 numberOfChoices, bool jumped, bytes extraData)" + + const getFunctionReturnParameterNames = (abi: AbiFunction[], name: string): string[] => { + const f = abi.filter((abi: AbiFunction) => abi.type === "function" && abi.name === name)[0]; // WARNING: overloaded functions confusion + return f.outputs.map((item) => item.name).filter(String) as string[]; + }; + + const createObject = (keys: string[], values: any[]) => Object.fromEntries(keys.map((k, i) => [k, values[i]])); + + const disputes = await disputeKit.read.disputes([BigInt(0)]); + const disputeParamNames = getFunctionReturnParameterNames(disputeKit.abi as unknown as AbiFunction[], "disputes"); // such type hack + const disputeObject = createObject(disputeParamNames, [...disputes]); + console.log("disputes: %O", disputeObject); +}; + +main() + .then(() => process.exit(0)) + .catch((error) => { + console.error(error); + process.exit(1); + }); diff --git a/contracts/src/arbitration/arbitrables/Escrow.sol b/contracts/src/arbitration/arbitrables/Escrow.sol index 47650d247..aee794a65 100644 --- a/contracts/src/arbitration/arbitrables/Escrow.sol +++ b/contracts/src/arbitration/arbitrables/Escrow.sol @@ -84,14 +84,20 @@ contract Escrow is IArbitrableV2 { /// @dev Emitted when a transaction is created. /// @param _transactionID The index of the transaction. + /// @param _transactionUri The IPFS Uri Hash of the transaction. /// @param _buyer The address of the buyer. /// @param _seller The address of the seller. /// @param _amount The initial amount in the transaction. + /// @param _asset The asset used ("native" for native chain token). + /// @param _deadline The deadline of the transaction. event TransactionCreated( uint256 indexed _transactionID, + string _transactionUri, address indexed _buyer, address indexed _seller, - uint256 _amount + uint256 _amount, + string _asset, + uint256 _deadline ); /// @dev To be emitted when a transaction is resolved, either by its @@ -166,12 +172,14 @@ contract Escrow is IArbitrableV2 { /// @dev Create a transaction. /// @param _timeoutPayment Time after which a party can automatically execute the arbitrable transaction. + /// @param _transactionUri The IPFS Uri Hash of the transaction. /// @param _seller The recipient of the transaction. /// @param _templateData The dispute template data. /// @param _templateDataMappings The dispute template data mappings. /// @return transactionID The index of the transaction. function createTransaction( uint256 _timeoutPayment, + string memory _transactionUri, address payable _seller, string memory _templateData, string memory _templateDataMappings @@ -186,7 +194,15 @@ contract Escrow is IArbitrableV2 { transactionID = transactions.length - 1; - emit TransactionCreated(transactionID, msg.sender, _seller, msg.value); + emit TransactionCreated( + transactionID, + _transactionUri, + msg.sender, + _seller, + msg.value, + "native", + transaction.deadline + ); } /// @dev Pay seller. To be called if the good or service is provided. diff --git a/subgraph/core/src/entities/ClassicRound.ts b/subgraph/core/src/entities/ClassicRound.ts index 8b28aae7e..39670fdd1 100644 --- a/subgraph/core/src/entities/ClassicRound.ts +++ b/subgraph/core/src/entities/ClassicRound.ts @@ -25,15 +25,15 @@ class CurrentRulingInfo { tied: boolean; } -export function updateCountsAndGetCurrentRuling(id: string, choice: BigInt, choiceCount: BigInt): CurrentRulingInfo { +export function updateCountsAndGetCurrentRuling(id: string, choice: BigInt, delta: BigInt): CurrentRulingInfo { const round = ClassicRound.load(id); if (!round) return { ruling: ZERO, tied: false }; const choiceNum = choice.toI32(); - const delta = choiceCount.minus(round.counts[choiceNum]); + const newChoiceCount = round.counts[choiceNum].plus(delta); let newCounts: BigInt[] = []; for (let i = 0; i < round.counts.length; i++) { if (BigInt.fromI32(i).equals(choice)) { - newCounts.push(choiceCount); + newCounts.push(newChoiceCount); } else { newCounts.push(round.counts[i]); } @@ -43,9 +43,9 @@ export function updateCountsAndGetCurrentRuling(id: string, choice: BigInt, choi if (choice.equals(round.winningChoice)) { if (round.tied) round.tied = false; } else { - if (choiceCount.equals(currentWinningCount)) { + if (newChoiceCount.equals(currentWinningCount)) { if (!round.tied) round.tied = true; - } else if (choiceCount.gt(currentWinningCount)) { + } else if (newChoiceCount.gt(currentWinningCount)) { round.winningChoice = choice; round.tied = false; } diff --git a/web/src/app.tsx b/web/src/app.tsx index a1f35108d..97c05cfd8 100644 --- a/web/src/app.tsx +++ b/web/src/app.tsx @@ -14,6 +14,8 @@ import Cases from "./pages/Cases"; import Dashboard from "./pages/Dashboard"; import Courts from "./pages/Courts"; import DisputeTemplateView from "./pages/DisputeTemplateView"; +import DisputeResolver from "./pages/Resolver"; +import { NewDisputeProvider } from "./context/NewDisputeContext"; const App: React.FC = () => { return ( @@ -22,16 +24,19 @@ const App: React.FC = () => { - - }> - } /> - } /> - } /> - } /> - } /> - Justice not found here ¯\_( ͡° ͜ʖ ͡°)_/¯} /> - - + + + }> + } /> + } /> + } /> + } /> + } /> + } /> + Justice not found here ¯\_( ͡° ͜ʖ ͡°)_/¯} /> + + + diff --git a/web/src/assets/svgs/icons/dispute.svg b/web/src/assets/svgs/icons/dispute.svg new file mode 100644 index 000000000..e3622bf1d --- /dev/null +++ b/web/src/assets/svgs/icons/dispute.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/web/src/assets/svgs/icons/ellipse.svg b/web/src/assets/svgs/icons/ellipse.svg new file mode 100644 index 000000000..05629b571 --- /dev/null +++ b/web/src/assets/svgs/icons/ellipse.svg @@ -0,0 +1,3 @@ + + + diff --git a/web/src/assets/svgs/icons/minus.svg b/web/src/assets/svgs/icons/minus.svg new file mode 100644 index 000000000..76b4d2a91 --- /dev/null +++ b/web/src/assets/svgs/icons/minus.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/src/assets/svgs/icons/plus.svg b/web/src/assets/svgs/icons/plus.svg new file mode 100644 index 000000000..7a1ce9bc3 --- /dev/null +++ b/web/src/assets/svgs/icons/plus.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/web/src/components/CasesDisplay/CasesGrid.tsx b/web/src/components/CasesDisplay/CasesGrid.tsx index 3f8d2bfe6..d2a95598a 100644 --- a/web/src/components/CasesDisplay/CasesGrid.tsx +++ b/web/src/components/CasesDisplay/CasesGrid.tsx @@ -1,14 +1,13 @@ -import React, { useMemo } from "react"; +import React from "react"; import styled from "styled-components"; -import { useWindowSize } from "react-use"; import { useParams } from "react-router-dom"; import { SkeletonDisputeCard, SkeletonDisputeListItem } from "../StyledSkeleton"; import { StandardPagination } from "@kleros/ui-components-library"; -import { BREAKPOINT_LANDSCAPE } from "styles/landscapeStyle"; import { useIsList } from "context/IsListProvider"; import { isUndefined } from "utils/index"; import { decodeURIFilter } from "utils/uri"; import { DisputeDetailsFragment } from "queries/useCasesQuery"; +import useIsDesktop from "hooks/useIsDesktop"; import DisputeCard from "components/DisputeCard"; import CasesListHeader from "./CasesListHeader"; @@ -46,12 +45,11 @@ const CasesGrid: React.FC = ({ disputes, casesPerPage, totalPages, c const decodedFilter = decodeURIFilter(filter ?? "all"); const { id: searchValue } = decodedFilter; const { isList } = useIsList(); - const { width } = useWindowSize(); - const screenIsBig = useMemo(() => width > BREAKPOINT_LANDSCAPE, [width]); + const isDesktop = useIsDesktop(); return ( <> - {isList && screenIsBig ? ( + {isList && isDesktop ? ( {isUndefined(disputes) diff --git a/web/src/components/CasesDisplay/CasesListHeader.tsx b/web/src/components/CasesDisplay/CasesListHeader.tsx index ad19963b2..bc3edb305 100644 --- a/web/src/components/CasesDisplay/CasesListHeader.tsx +++ b/web/src/components/CasesDisplay/CasesListHeader.tsx @@ -6,25 +6,23 @@ import { responsiveSize } from "styles/responsiveSize"; const Container = styled.div` display: flex; - justify-content: space-between; - gap: calc(15vw + (40 - 15) * (min(max(100vw, 375px), 1250px) - 375px) / 875); width: 100%; height: 100%; + justify-content: space-between; `; const CasesData = styled.div` - display: flex; - align-items: center; - justify-content: space-around; - width: 100%; - margin-left: ${responsiveSize(0, 33)}; - flex-wrap: wrap; - padding: 0 3%; - gap: ${responsiveSize(24, 48, 300)}; + display: grid; + flex: 1; + grid-template-columns: repeat(4, ${responsiveSize(100, 130, 900)}); + column-gap: ${responsiveSize(2, 12, 900)}; + justify-content: space-between; + margin-right: 8px; `; const CaseTitle = styled.div` display: none; + width: ${responsiveSize(270, 345, 900)}; margin-left: 32px; gap: 36px; label { @@ -42,9 +40,7 @@ const CaseTitle = styled.div` )} `; -const StyledLabel = styled.label` - margin-left: ${responsiveSize(4, 8, 300, 900)}; -`; +const StyledLabel = styled.label``; const tooltipMsg = "Users have an economic interest in serving as jurors in Kleros: " + diff --git a/web/src/components/CasesDisplay/Filters.tsx b/web/src/components/CasesDisplay/Filters.tsx index 01b5cc639..ebc332eaa 100644 --- a/web/src/components/CasesDisplay/Filters.tsx +++ b/web/src/components/CasesDisplay/Filters.tsx @@ -1,12 +1,11 @@ import React from "react"; import styled, { useTheme } from "styled-components"; import { useNavigate, useParams } from "react-router-dom"; -import { useWindowSize } from "react-use"; import { DropdownSelect } from "@kleros/ui-components-library"; import { useIsList } from "context/IsListProvider"; +import useIsDesktop from "hooks/useIsDesktop"; import ListIcon from "svgs/icons/list.svg"; import GridIcon from "svgs/icons/grid.svg"; -import { BREAKPOINT_LANDSCAPE } from "styles/landscapeStyle"; import { decodeURIFilter, encodeURIFilter, useRootPath } from "utils/uri"; const Container = styled.div` @@ -59,9 +58,8 @@ const Filters: React.FC = () => { navigate(`${location}/1/${value}/${encodedFilter}`); }; - const { width } = useWindowSize(); const { isList, setIsList } = useIsList(); - const screenIsBig = width > BREAKPOINT_LANDSCAPE; + const isDesktop = useIsDesktop(); return ( @@ -87,14 +85,14 @@ const Filters: React.FC = () => { defaultValue={order} callback={handleOrderChange} /> - {screenIsBig ? ( + {isDesktop ? ( {isList ? ( setIsList(false)} /> ) : ( { - if (screenIsBig) { + if (isDesktop) { setIsList(true); } }} diff --git a/web/src/components/ConnectWallet/AccountDisplay.tsx b/web/src/components/ConnectWallet/AccountDisplay.tsx index 1d677637e..80b5e43d6 100644 --- a/web/src/components/ConnectWallet/AccountDisplay.tsx +++ b/web/src/components/ConnectWallet/AccountDisplay.tsx @@ -4,6 +4,7 @@ import { landscapeStyle } from "styles/landscapeStyle"; import { useAccount, useNetwork, useEnsAvatar, useEnsName } from "wagmi"; import Identicon from "react-identicons"; import { shortenAddress } from "utils/shortenAddress"; +import { isAddress } from "viem"; const Container = styled.div` display: flex; @@ -134,7 +135,7 @@ export const AddressOrName: React.FC = ({ address: propAddress } chainId: 1, }); - return ; + return ; }; export const ChainDisplay: React.FC = () => { diff --git a/web/src/components/DisputeCard/DisputeInfo.tsx b/web/src/components/DisputeCard/DisputeInfo.tsx index b37550f04..cf7b431b8 100644 --- a/web/src/components/DisputeCard/DisputeInfo.tsx +++ b/web/src/components/DisputeCard/DisputeInfo.tsx @@ -27,6 +27,7 @@ const Container = styled.div<{ isList: boolean; isOverview?: boolean }>` () => css` gap: 0; height: 100%; + flex: 1; ` )} `}; @@ -56,9 +57,11 @@ const RestOfFieldsContainer = styled.div<{ isList?: boolean; isOverview?: boolea css` ${landscapeStyle( () => css` - flex-direction: row; - gap: ${responsiveSize(4, 24, 300, 900)}; - justify-content: space-around; + display: grid; + grid-template-columns: repeat(4, ${responsiveSize(100, 130, 900)}); + column-gap: ${responsiveSize(2, 12, 900)}; + justify-content: space-between; + align-items: center; ` )} `}; diff --git a/web/src/components/DisputeCard/PeriodBanner.tsx b/web/src/components/DisputeCard/PeriodBanner.tsx index d85d681c7..dc62b3ff9 100644 --- a/web/src/components/DisputeCard/PeriodBanner.tsx +++ b/web/src/components/DisputeCard/PeriodBanner.tsx @@ -1,10 +1,11 @@ import React from "react"; import styled, { Theme } from "styled-components"; import { Periods } from "consts/periods"; +import { responsiveSize } from "styles/responsiveSize"; const Container = styled.div>` height: ${({ isCard }) => (isCard ? "45px" : "100%")}; - width: auto; + width: ${({ isCard }) => (isCard ? "auto" : responsiveSize(60, 80, 900))}; border-top-right-radius: 3px; border-top-left-radius: 3px; display: flex; diff --git a/web/src/components/DisputeCard/index.tsx b/web/src/components/DisputeCard/index.tsx index 1d51d99a6..de0d4b955 100644 --- a/web/src/components/DisputeCard/index.tsx +++ b/web/src/components/DisputeCard/index.tsx @@ -68,7 +68,7 @@ const ListTitle = styled.div` height: 100%; justify-content: start; align-items: center; - width: calc(30vw + (40 - 30) * (min(max(100vw, 300px), 1250px)- 300px) / 950); + width: ${responsiveSize(240, 300, 900)}; `; export const getPeriodEndTimestamp = ( diff --git a/web/src/components/DisputePreview/Alias.tsx b/web/src/components/DisputePreview/Alias.tsx new file mode 100644 index 000000000..53355aa4f --- /dev/null +++ b/web/src/components/DisputePreview/Alias.tsx @@ -0,0 +1,50 @@ +import React from "react"; +import styled from "styled-components"; +import { AddressOrName, IdenticonOrAvatar } from "../ConnectWallet/AccountDisplay"; +import { Alias } from "context/NewDisputeContext"; +import { isUndefined } from "utils/index"; +import { useEnsAddress } from "wagmi"; +import { isAddress } from "viem"; +import Skeleton from "react-loading-skeleton"; + +const AliasContainer = styled.div` + min-height: 32px; + display: flex; + gap: 8px; + align-items: center; +`; + +const TextContainer = styled.div` + display: flex; + > label { + color: ${({ theme }) => theme.primaryText}; + font-size: 14px; + } +`; + +interface IAlias { + alias: Alias; +} + +const AliasDisplay: React.FC = ({ alias }) => { + const { data: addressFromENS, isLoading } = useEnsAddress({ + enabled: !isAddress(alias.address), // if alias.address is not an Address, we treat it as ENS and try to fetch address from there + name: alias.address, + chainId: 1, + }); + + // try fetching ens name, else go with address + const address = addressFromENS ?? alias.address; + + return ( + + {isLoading ? : } + + {isLoading ? : }  + {!isUndefined(alias.name) && alias.name !== "" ? : null} + + + ); +}; + +export default AliasDisplay; diff --git a/web/src/pages/Cases/CaseDetails/Overview/DisputeContext.tsx b/web/src/components/DisputePreview/DisputeContext.tsx similarity index 63% rename from web/src/pages/Cases/CaseDetails/Overview/DisputeContext.tsx rename to web/src/components/DisputePreview/DisputeContext.tsx index f1367755c..65d0c1217 100644 --- a/web/src/pages/Cases/CaseDetails/Overview/DisputeContext.tsx +++ b/web/src/components/DisputePreview/DisputeContext.tsx @@ -3,6 +3,9 @@ import ReactMarkdown from "components/ReactMarkdown"; import styled from "styled-components"; import { StyledSkeleton } from "components/StyledSkeleton"; import { isUndefined } from "utils/index"; +import { Answer as IAnswer, IDisputeTemplate } from "context/NewDisputeContext"; +import AliasDisplay from "./Alias"; +import { responsiveSize } from "styles/responsiveSize"; const StyledH1 = styled.h1` margin: 0; @@ -11,6 +14,10 @@ const StyledH1 = styled.h1` const QuestionAndDescription = styled.div` display: flex; flex-direction: column; + div:first-child p:first-of-type { + font-size: 16px; + font-weight: 600; + } `; const StyledReactMarkDown = styled(ReactMarkdown)` @@ -31,32 +38,24 @@ const AnswersContainer = styled.div` const Answer = styled.div` margin: 0px; display: flex; - gap: 8px; + flex-wrap: wrap; + gap: ${responsiveSize(2, 8)}; `; -interface IAnswer { - id?: string; - title: string; - description?: string; - reserved?: boolean; -} - -interface IDisputeTemplate { - answers: IAnswer[]; - arbitrableAddress: string; - arbitrableChainID: string; - arbitratorAddress: string; - arbitratorChainID: string; - category?: string; - description: string; - frontendUrl?: string; - lang?: string; - policyURI?: string; - question: string; - specification?: string; - title: string; -} +const AliasesContainer = styled.div` + display: flex; + flex-wrap: wrap; + gap: ${responsiveSize(8, 20)}; +`; +const Divider = styled.hr` + width: 100%; + display: flex; + border: none; + height: 1px; + background-color: ${({ theme }) => theme.stroke}; + margin: 0; +`; interface IDisputeContext { disputeTemplate: IDisputeTemplate; } @@ -86,13 +85,27 @@ export const DisputeContext: React.FC = ({ disputeTemplate }) = {isUndefined(disputeTemplate) ? null :

Voting Options

} {disputeTemplate?.answers?.map((answer: IAnswer, i: number) => ( - + Option {i + 1}: - + ))} + + {isUndefined(disputeTemplate?.aliases) ? null : ( + <> + + + {disputeTemplate.aliases.map((alias) => ( + + ))} + + + )} ); }; diff --git a/web/src/pages/Cases/CaseDetails/Overview/Policies.tsx b/web/src/components/DisputePreview/Policies.tsx similarity index 100% rename from web/src/pages/Cases/CaseDetails/Overview/Policies.tsx rename to web/src/components/DisputePreview/Policies.tsx diff --git a/web/src/pages/Home/HeroImage.tsx b/web/src/components/HeroImage.tsx similarity index 72% rename from web/src/pages/Home/HeroImage.tsx rename to web/src/components/HeroImage.tsx index 3170244b5..d07c177f9 100644 --- a/web/src/pages/Home/HeroImage.tsx +++ b/web/src/components/HeroImage.tsx @@ -1,18 +1,16 @@ import React from "react"; import { useTheme } from "styled-components"; -import { useWindowSize } from "react-use"; -import { BREAKPOINT_LANDSCAPE } from "styles/landscapeStyle"; import HeroLightMobile from "tsx:svgs/hero/hero-lightmode-mobile.svg"; import HeroDarkMobile from "tsx:svgs/hero/hero-darkmode-mobile.svg"; import HeroLightDesktop from "tsx:svgs/hero/hero-lightmode-desktop.svg"; import HeroDarkDesktop from "tsx:svgs/hero/hero-darkmode-desktop.svg"; +import useIsDesktop from "hooks/useIsDesktop"; const HeroImage = () => { - const { width } = useWindowSize(); const theme = useTheme(); const themeIsLight = theme.name === "light"; - const screenIsBig = width > BREAKPOINT_LANDSCAPE; - return
{screenIsBig ? : }
; + const isDesktop = useIsDesktop(); + return
{isDesktop ? : }
; }; const HeroDesktop: React.FC<{ themeIsLight: boolean }> = ({ themeIsLight }) => { diff --git a/web/src/components/InfoCard.tsx b/web/src/components/InfoCard.tsx new file mode 100644 index 000000000..17ff32dbd --- /dev/null +++ b/web/src/components/InfoCard.tsx @@ -0,0 +1,30 @@ +import React from "react"; +import styled from "styled-components"; +import { responsiveSize } from "styles/responsiveSize"; +import InfoCircle from "tsx:svgs/icons/info-circle.svg"; + +const InfoContainer = styled.div` + display: grid; + grid-template-columns: 16px max-content; + gap: ${responsiveSize(4, 8, 300)}; + align-items: center; + justify-items: center; + text-align: center; + color: ${({ theme }) => theme.secondaryText}; +`; + +interface IInfoCard { + msg: string; + className?: string; +} + +const InfoCard: React.FC = ({ msg, className }) => { + return ( + + + {msg} + + ); +}; + +export default InfoCard; diff --git a/web/src/components/LabeledInput.tsx b/web/src/components/LabeledInput.tsx new file mode 100644 index 000000000..b7fb5958c --- /dev/null +++ b/web/src/components/LabeledInput.tsx @@ -0,0 +1,36 @@ +import { Field, FieldProps } from "@kleros/ui-components-library"; +import React from "react"; +import styled from "styled-components"; +import { isUndefined } from "utils/index"; + +const Container = styled.div` + width: 100%; + display: flex; + flex-direction: column; +`; +const StyledField = styled(Field)` + width: 100%; + > small { + margin-top: 16px; + margin-bottom: 16px; + } +`; + +const StyledLabel = styled.label` + width: 100%; + margin-bottom: 12px; +`; + +interface ILabeledInput extends FieldProps { + label?: string; +} +const LabeledInput: React.FC = (props) => { + return ( + + {!isUndefined(props.label) ? {props.label} : null} + + + ); +}; + +export default LabeledInput; diff --git a/web/src/components/PlusMinusField.tsx b/web/src/components/PlusMinusField.tsx new file mode 100644 index 000000000..7ef2a679c --- /dev/null +++ b/web/src/components/PlusMinusField.tsx @@ -0,0 +1,63 @@ +import React from "react"; +import styled, { css } from "styled-components"; +import Ellipse from "assets/svgs/icons/ellipse.svg"; +import Plus from "assets/svgs/icons/plus.svg"; +import Minus from "assets/svgs/icons/minus.svg"; + +const Container = styled.div` + display: flex; + gap: 8px; + margin: 32px 0px 48px; +`; + +const IconContainer = styled.button` + position: relative; + padding: 0; + border-radius: 50%; + border: none; + background-color: transparent; + cursor: pointer; +`; + +const StyledEllipseIcon = styled(Ellipse)<{ isDisabled?: boolean }>` + circle { + ${({ isDisabled }) => + isDisabled && + css` + fill-opacity: 0.12; + `}; + } +`; + +const Icon = styled.svg` + fill: ${({ theme }) => theme.white}; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); +`; + +interface IPlusMinusField { + currentValue: number; + updateValue: (currentValue: number) => void; + minValue?: number; + className?: string; +} +const PlusMinusField: React.FC = ({ currentValue, updateValue, minValue = 0, className }) => { + const incrementValue = () => updateValue(++currentValue); + const decrementValue = () => currentValue > minValue && updateValue(--currentValue); + return ( + + + + + + + + + + + ); +}; + +export default PlusMinusField; diff --git a/web/src/components/Popup/Description/DisputeCreated.tsx b/web/src/components/Popup/Description/DisputeCreated.tsx new file mode 100644 index 000000000..22d4a10d7 --- /dev/null +++ b/web/src/components/Popup/Description/DisputeCreated.tsx @@ -0,0 +1,65 @@ +import React, { useMemo } from "react"; +import Skeleton from "react-loading-skeleton"; +import styled from "styled-components"; +import { responsiveSize } from "styles/responsiveSize"; +import { isUndefined } from "utils/index"; +import { formatDate, getCurrentTime } from "utils/date"; +import { useCourtDetails } from "hooks/queries/useCourtDetails"; + +const Container = styled.div` + display: flex; + flex-direction: column; + margin-bottom: 24px; +`; + +const StyledTitle = styled.div` + margin-left: ${responsiveSize(8, 44, 300)}; + margin-right: ${responsiveSize(8, 44, 300)}; + color: ${({ theme }) => theme.secondaryText}; + text-align: center; +`; + +const StyledDateContainer = styled.span` + color: ${({ theme }) => theme.primaryText}; +`; + +const StyledSubtitle = styled(StyledTitle)` + margin-top: 24px; + color: ${({ theme }) => theme.primaryText}; +`; +interface IDisputeCreated { + courtId: string; +} + +const DisputeCreated: React.FC = ({ courtId }) => { + const { data: courtDetails } = useCourtDetails(courtId); + + const date = useMemo( + () => + !isUndefined(courtDetails?.court?.timesPerPeriod) + ? calculateMinResolveTime(courtDetails?.court.timesPerPeriod) + : undefined, + [courtDetails] + ); + + return ( + + + 🎉 Your case was successfully submitted to Kleros. A pool of jurors will be drawn to evaluate the case and vote + at most{" "} + {isUndefined(date) ? ( + + ) : ( + {formatDate(date)} + )} + . 🎉 + + Now, it’s time to submit evidence to support the case. + + ); +}; + +const calculateMinResolveTime = (timesPerPeriod: string[]) => + timesPerPeriod.reduce((acc, val) => acc + parseInt(val), 0) + getCurrentTime(); + +export default DisputeCreated; diff --git a/web/src/components/Popup/ExtraInfo/DisputeCreatedExtraInfo.tsx b/web/src/components/Popup/ExtraInfo/DisputeCreatedExtraInfo.tsx new file mode 100644 index 000000000..c9f154be4 --- /dev/null +++ b/web/src/components/Popup/ExtraInfo/DisputeCreatedExtraInfo.tsx @@ -0,0 +1,23 @@ +import React from "react"; +import styled from "styled-components"; +import { responsiveSize } from "styles/responsiveSize"; + +const Container = styled.div` + display: flex; + color: ${({ theme }) => theme.secondaryText}; + text-align: center; + margin-top: ${responsiveSize(8, 24, 300)}; + margin-right: ${responsiveSize(8, 44, 300)}; + margin-left: ${responsiveSize(8, 44, 300)}; +`; + +const DisputeCreatedExtraInfo: React.FC = () => { + return ( + + { + "In order to better track the case progress, we recommend you to subscribe to notifications: Settings > Notifications" + } + + ); +}; +export default DisputeCreatedExtraInfo; diff --git a/web/src/components/Popup/ExtraInfo/VoteWithCommitExtraInfo.tsx b/web/src/components/Popup/ExtraInfo/VoteWithCommitExtraInfo.tsx index 11003f32a..b0a8a096f 100644 --- a/web/src/components/Popup/ExtraInfo/VoteWithCommitExtraInfo.tsx +++ b/web/src/components/Popup/ExtraInfo/VoteWithCommitExtraInfo.tsx @@ -1,37 +1,16 @@ import React from "react"; import styled from "styled-components"; -import InfoCircle from "tsx:svgs/icons/info-circle.svg"; +import InfoCard from "components/InfoCard"; import { responsiveSize } from "styles/responsiveSize"; -const Container = styled.div` - display: flex; - gap: 4px; - text-align: center; +const StyledInfoCard = styled(InfoCard)` margin: ${responsiveSize(8, 24, 300)} ${responsiveSize(8, 32, 300)} 0; font-size: 14px; font-weight: 400; line-height: 19px; - color: ${({ theme }) => theme.secondaryText}; -`; - -const InfoCircleContainer = styled.div` - display: flex; - margin-top: 2px; - - svg { - min-width: 16px; - min-height: 16px; - } `; const VoteWithCommitExtraInfo: React.FC = () => { - return ( - - - - - Subscribe to receive notifications to be reminded when the reveal time comes. - - ); + return ; }; export default VoteWithCommitExtraInfo; diff --git a/web/src/components/Popup/index.tsx b/web/src/components/Popup/index.tsx index e1b285230..ea19f7007 100644 --- a/web/src/components/Popup/index.tsx +++ b/web/src/components/Popup/index.tsx @@ -10,6 +10,9 @@ import Appeal from "./Description/Appeal"; import VoteWithCommitExtraInfo from "./ExtraInfo/VoteWithCommitExtraInfo"; import StakeWithdrawExtraInfo from "./ExtraInfo/StakeWithdrawExtraInfo"; import { responsiveSize } from "styles/responsiveSize"; +import DisputeCreated from "./Description/DisputeCreated"; +import DisputeCreatedExtraInfo from "./ExtraInfo/DisputeCreatedExtraInfo"; +import { useNavigate } from "react-router-dom"; const Header = styled.h1` display: flex; @@ -92,6 +95,7 @@ export enum PopupType { APPEAL = "APPEAL", VOTE_WITHOUT_COMMIT = "VOTE_WITHOUT_COMMIT", VOTE_WITH_COMMIT = "VOTE_WITH_COMMIT", + DISPUTE_CREATED = "DISPUTE_CREATED", } interface IStakeWithdraw { @@ -117,6 +121,11 @@ interface IAppeal { amount: string; option: string; } +interface IDisputeCreated { + popupType: PopupType.DISPUTE_CREATED; + disputeId: number; + courtId: string; +} interface IPopup { title: string; icon: React.FC>; @@ -126,7 +135,7 @@ interface IPopup { isCommit?: boolean; } -type PopupProps = IStakeWithdraw | IVoteWithoutCommit | IVoteWithCommit | IAppeal; +type PopupProps = IStakeWithdraw | IVoteWithoutCommit | IVoteWithCommit | IAppeal | IDisputeCreated; const Popup: React.FC = ({ title, @@ -138,6 +147,7 @@ const Popup: React.FC = ({ ...props }) => { const containerRef = useRef(null); + const navigate = useNavigate(); const resetValue = () => { if (setAmount) { @@ -178,6 +188,11 @@ const Popup: React.FC = ({ PopupComponent = ; break; } + case PopupType.DISPUTE_CREATED: { + const { courtId } = props as IDisputeCreated; + PopupComponent = ; + break; + } default: break; } @@ -193,12 +208,17 @@ const Popup: React.FC = ({ {popupType === PopupType.STAKE_WITHDRAW && } {popupType === PopupType.VOTE_WITH_COMMIT && } + {popupType === PopupType.DISPUTE_CREATED && } { setIsOpen(false); resetValue(); + if (popupType === PopupType.DISPUTE_CREATED) { + const { disputeId } = props as IDisputeCreated; + navigate(`/cases/${disputeId}`); + } }} />
diff --git a/web/src/components/StyledSkeleton.tsx b/web/src/components/StyledSkeleton.tsx index a38cb6906..e8a4448b2 100644 --- a/web/src/components/StyledSkeleton.tsx +++ b/web/src/components/StyledSkeleton.tsx @@ -32,9 +32,13 @@ const StyledSkeletonDisputeListItem = styled(Skeleton)` height: 62px; `; -const StyledSkeletonEvidenceCard = styled(Skeleton)` - height: 146px; - width: 76vw; +const StyledSkeletonEvidenceContainer = styled.div` + width: 100%; + span { + width: 100%; + height: 146px; + display: flex; + } `; export const SkeletonDisputeCard = () => ( @@ -45,4 +49,8 @@ export const SkeletonDisputeCard = () => ( export const SkeletonDisputeListItem = () => ; -export const SkeletonEvidenceCard = () => ; +export const SkeletonEvidenceCard = () => ( + + + +); diff --git a/web/src/consts/eip712-messages.ts b/web/src/consts/eip712-messages.ts index 01ba1494d..4e5f05f50 100644 --- a/web/src/consts/eip712-messages.ts +++ b/web/src/consts/eip712-messages.ts @@ -1,11 +1,19 @@ +import { arbitrumSepolia } from "viem/chains"; + export default { - contactDetails: (address: `0x${string}`, nonce, telegram = "", email = "") => + contactDetails: ( + address: `0x${string}`, + nonce: string, + telegram = "", + email = "", + chainId: number = arbitrumSepolia.id + ) => ({ address: address.toLowerCase() as `0x${string}`, domain: { name: "Kleros v2", version: "1", - chainId: 421_613, + chainId, }, types: { ContactDetails: [ @@ -21,4 +29,23 @@ export default { nonce, }, } as const), + signingAccount: (address: `0x${string}`, chainId: number = arbitrumSepolia.id) => + ({ + account: address.toLowerCase() as `0x${string}`, + domain: { + name: "Kleros v2", + version: "1", + chainId, + }, + types: { + SigningAccount: [{ name: "body", type: "string" }], + }, + primaryType: "SigningAccount", + message: { + body: + "To keep your data safe and to use certain features of Kleros, we ask that you sign these message to " + + "create a secret key for your account. This key is unrelated from your main Ethereum account and will " + + "not be able to send any transactions.", + }, + } as const), }; diff --git a/web/src/context/NewDisputeContext.tsx b/web/src/context/NewDisputeContext.tsx new file mode 100644 index 000000000..6e992ed4b --- /dev/null +++ b/web/src/context/NewDisputeContext.tsx @@ -0,0 +1,121 @@ +import React, { createContext, useState, useContext, useMemo } from "react"; +import { isUndefined } from "utils/index"; +import { Address } from "viem"; +import { useLocalStorage } from "hooks/useLocalStorage"; + +export type Answer = { + id?: string; + title: string; + description?: string; + reserved?: boolean; +}; + +export type Alias = { + id?: string; + name: string; + address: string | Address; + isValid?: boolean; +}; + +export interface IDisputeTemplate { + answers: Answer[]; + aliases?: Alias[]; + arbitrableAddress?: string; + arbitrableChainID?: string; + arbitratorAddress?: string; + arbitratorChainID?: string; + category?: string; + description: string; + frontendUrl?: string; + lang?: string; + policyURI?: string; + question: string; + specification?: string; + title: string; +} + +interface IDisputeData extends IDisputeTemplate { + courtId?: string; + numberOfJurors: number; + arbitrationCost?: string; +} + +interface INewDisputeContext { + disputeData: IDisputeData; + setDisputeData: (disputeData: IDisputeData) => void; + disputeTemplate: IDisputeTemplate; + resetDisputeData: () => void; + isSubmittingCase: boolean; + setIsSubmittingCase: (isSubmittingCase: boolean) => void; + isPolicyUploading: boolean; + setIsPolicyUploading: (isPolicyUploading: boolean) => void; +} + +const initialDisputeData: IDisputeData = { + numberOfJurors: 3, + title: "", + description: "", + question: "", + answers: [ + { title: "", id: "1" }, + { title: "", id: "2" }, + ], + aliases: [ + { name: "", address: "", id: "1" }, + { name: "", address: "", id: "2" }, + ], +}; +const initialDisputeTemplate = initialDisputeData as IDisputeTemplate; + +const NewDisputeContext = createContext({ + disputeData: initialDisputeData, + setDisputeData: () => {}, + disputeTemplate: initialDisputeTemplate, + resetDisputeData: () => {}, + isSubmittingCase: false, + setIsSubmittingCase: () => {}, + isPolicyUploading: false, + setIsPolicyUploading: () => {}, +}); + +export const useNewDisputeContext = () => useContext(NewDisputeContext); + +export const NewDisputeProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => { + const [disputeData, setDisputeData] = useLocalStorage("disputeData", initialDisputeData); + const [isSubmittingCase, setIsSubmittingCase] = useState(false); + const [isPolicyUploading, setIsPolicyUploading] = useState(false); + + const disputeTemplate = useMemo(() => constructDisputeTemplate(disputeData), [disputeData]); + + const resetDisputeData = () => { + setDisputeData(initialDisputeData); + }; + + const contextValues = useMemo( + () => ({ + disputeData, + setDisputeData, + disputeTemplate, + resetDisputeData, + isSubmittingCase, + setIsSubmittingCase, + isPolicyUploading, + setIsPolicyUploading, + }), + [disputeData, disputeTemplate, resetDisputeData, isSubmittingCase, isPolicyUploading] + ); + + return {children}; +}; + +const constructDisputeTemplate = (disputeData: IDisputeData) => { + const baseTemplate = { ...disputeData } as IDisputeTemplate; + + if (!isUndefined(baseTemplate.aliases)) { + baseTemplate.aliases = baseTemplate.aliases.filter((item) => item.address !== "" && item.isValid); + if (baseTemplate.aliases.length === 0) delete baseTemplate.aliases; + } + if (!isUndefined(baseTemplate.policyURI) && baseTemplate.policyURI === "") delete baseTemplate.policyURI; + + return baseTemplate; +}; diff --git a/web/src/hooks/queries/useCourtDetails.ts b/web/src/hooks/queries/useCourtDetails.ts index 14c708e34..0cbf5546c 100644 --- a/web/src/hooks/queries/useCourtDetails.ts +++ b/web/src/hooks/queries/useCourtDetails.ts @@ -17,6 +17,7 @@ const courtDetailsQuery = graphql(` stake paidETH paidPNK + timesPerPeriod } } `); diff --git a/web/src/hooks/queries/useDrawQuery.ts b/web/src/hooks/queries/useDrawQuery.ts index 7f69184fa..f7219442d 100644 --- a/web/src/hooks/queries/useDrawQuery.ts +++ b/web/src/hooks/queries/useDrawQuery.ts @@ -8,6 +8,12 @@ const drawQuery = graphql(` query Draw($address: String, $disputeID: String, $roundID: String) { draws(first: 1000, where: { dispute: $disputeID, juror: $address, round: $roundID }) { voteIDNum + vote { + ... on ClassicVote { + commit + commited + } + } } } `); diff --git a/web/src/hooks/useClassicAppealContext.tsx b/web/src/hooks/useClassicAppealContext.tsx index 706b8577e..067d30b2c 100644 --- a/web/src/hooks/useClassicAppealContext.tsx +++ b/web/src/hooks/useClassicAppealContext.tsx @@ -9,7 +9,11 @@ import { useClassicAppealQuery, ClassicAppealQuery } from "queries/useClassicApp import { useCountdown } from "hooks/useCountdown"; import { getLocalRounds } from "utils/getLocalRounds"; -const LoserSideCountdownContext = createContext(undefined); +interface ICountdownContext { + loserSideCountdown?: number; + winnerSideCountdown?: number; +} +const CountdownContext = createContext({}); const OptionsContext = createContext(undefined); @@ -60,6 +64,12 @@ export const ClassicAppealProvider: React.FC<{ dispute?.court.timesPerPeriod[Periods.appeal], multipliers?.loser_appeal_period_multiplier.toString() ); + + const winnerSideCountdown = useWinnerSideCountdown( + dispute?.lastPeriodChange, + dispute?.court.timesPerPeriod[Periods.appeal] + ); + const { loserRequiredFunding, winnerRequiredFunding } = useMemo( () => ({ loserRequiredFunding: getRequiredFunding(appealCost, multipliers?.loser_stake_multiplier), @@ -69,8 +79,11 @@ export const ClassicAppealProvider: React.FC<{ ); const fundedChoices = getFundedChoices(data?.dispute); const [selectedOption, setSelectedOption] = useState(); + return ( - + ({ loserSideCountdown, winnerSideCountdown }), [loserSideCountdown, winnerSideCountdown])} + > ({ selectedOption, setSelectedOption }), [selectedOption, setSelectedOption])} > @@ -89,11 +102,11 @@ export const ClassicAppealProvider: React.FC<{ {children} - + ); }; -export const useLoserSideCountdownContext = () => useContext(LoserSideCountdownContext); +export const useCountdownContext = () => useContext(CountdownContext); export const useSelectedOptionContext = () => useContext(SelectedOptionContext); export const useFundingContext = () => useContext(FundingContext); export const useOptionsContext = () => useContext(OptionsContext); @@ -136,6 +149,14 @@ function useLoserSideCountdown(lastPeriodChange = "0", appealPeriodDuration = "0 return useCountdown(deadline); } +function useWinnerSideCountdown(lastPeriodChange = "0", appealPeriodDuration = "0") { + const deadline = useMemo( + () => Number(BigInt(lastPeriodChange) + BigInt(appealPeriodDuration)), + [lastPeriodChange, appealPeriodDuration] + ); + return useCountdown(deadline); +} + const getDeadline = (lastPeriodChange: string, appealPeriodDuration: string, loserTimeMultiplier: string): number => { const parsedLastPeriodChange = BigInt(lastPeriodChange); const parsedAppealPeriodDuration = BigInt(appealPeriodDuration); diff --git a/web/src/hooks/useIsDesktop.tsx b/web/src/hooks/useIsDesktop.tsx new file mode 100644 index 000000000..1b0928f12 --- /dev/null +++ b/web/src/hooks/useIsDesktop.tsx @@ -0,0 +1,10 @@ +import { useMemo } from "react"; +import { useWindowSize } from "react-use"; +import { BREAKPOINT_LANDSCAPE } from "styles/landscapeStyle"; + +const useIsDesktop = () => { + const { width } = useWindowSize(); + return useMemo(() => width > BREAKPOINT_LANDSCAPE, [width]); +}; + +export default useIsDesktop; diff --git a/web/src/hooks/useSigningAccount.tsx b/web/src/hooks/useSigningAccount.tsx new file mode 100644 index 000000000..1966e3f47 --- /dev/null +++ b/web/src/hooks/useSigningAccount.tsx @@ -0,0 +1,27 @@ +import { useLocalStorage } from "react-use"; +import { Hex, WalletClient, keccak256 } from "viem"; +import { privateKeyToAccount } from "viem/accounts"; +import { useWalletClient } from "wagmi"; +import messages from "consts/eip712-messages"; +import { isUndefined } from "utils/index"; + +const useSigningAccount = () => { + const { data: wallet } = useWalletClient(); + const address = wallet?.account.address; + const key = `signingAccount-${address}`; + const [signingKey, setSigningKey] = useLocalStorage(key); + return { + signingAccount: !isUndefined(signingKey) ? privateKeyToAccount(signingKey) : undefined, + generateSigningAccount: () => (!isUndefined(wallet) ? generateSigningAccount(wallet, setSigningKey) : undefined), + }; +}; + +const generateSigningAccount = async (wallet: WalletClient, setSigningKey: (signingKey: `0x${string}`) => void) => { + if (isUndefined(wallet.account)) return; + const signature = await wallet.signTypedData(messages.signingAccount(wallet.account.address)); + const signingKey = keccak256(signature); + setSigningKey(signingKey); + return privateKeyToAccount(signingKey); +}; + +export default useSigningAccount; diff --git a/web/src/layout/Header/navbar/DappList.tsx b/web/src/layout/Header/navbar/DappList.tsx index eead0d5dd..bc584d5cd 100644 --- a/web/src/layout/Header/navbar/DappList.tsx +++ b/web/src/layout/Header/navbar/DappList.tsx @@ -106,7 +106,8 @@ const ITEMS = [ { text: "Resolver", Icon: Resolver, - url: "https://resolve.kleros.io", + url: "#/resolver", + isNewTab: false, }, { text: "Linguo", diff --git a/web/src/layout/Header/navbar/Product.tsx b/web/src/layout/Header/navbar/Product.tsx index a4ad9e43e..dc25e3ed4 100644 --- a/web/src/layout/Header/navbar/Product.tsx +++ b/web/src/layout/Header/navbar/Product.tsx @@ -38,11 +38,12 @@ interface IProduct { text: string; url: string; Icon: React.FC> | string; + isNewTab?: boolean; } -const Product: React.FC = ({ text, url, Icon }) => { +const Product: React.FC = ({ text, url, Icon, isNewTab = true }) => { return ( - + {typeof Icon === "string" ? : } {text} diff --git a/web/src/pages/Cases/CaseDetails/Appeal/Classic/Fund.tsx b/web/src/pages/Cases/CaseDetails/Appeal/Classic/Fund.tsx index 019e70bff..56ea16d14 100644 --- a/web/src/pages/Cases/CaseDetails/Appeal/Classic/Fund.tsx +++ b/web/src/pages/Cases/CaseDetails/Appeal/Classic/Fund.tsx @@ -1,4 +1,4 @@ -import React, { useState } from "react"; +import React, { useMemo, useState } from "react"; import styled from "styled-components"; import { useParams } from "react-router-dom"; import { useAccount, useBalance, usePublicClient } from "wagmi"; @@ -9,15 +9,12 @@ import { isUndefined } from "utils/index"; import { EnsureChain } from "components/EnsureChain"; import { usePrepareDisputeKitClassicFundAppeal, useDisputeKitClassicFundAppeal } from "hooks/contracts/generated"; import { useParsedAmount } from "hooks/useParsedAmount"; -import { - useLoserSideCountdownContext, - useSelectedOptionContext, - useFundingContext, -} from "hooks/useClassicAppealContext"; +import { useSelectedOptionContext, useFundingContext, useCountdownContext } from "hooks/useClassicAppealContext"; const Container = styled.div` display: flex; flex-direction: column; + align-items: center; gap: 8px; `; @@ -38,11 +35,14 @@ const StyledField = styled(Field)` const StyledButton = styled(Button)` margin: auto; - margin-top: 12px; + margin-top: 4px; `; +const StyledLabel = styled.label` + align-self: flex-start; +`; const useNeedFund = () => { - const loserSideCountdown = useLoserSideCountdownContext(); + const { loserSideCountdown } = useCountdownContext(); const { fundedChoices, winningChoice } = useFundingContext(); const needFund = (loserSideCountdown ?? 0) > 0 || @@ -57,7 +57,7 @@ const useNeedFund = () => { const useFundAppeal = (parsedAmount) => { const { id } = useParams(); const { selectedOption } = useSelectedOptionContext(); - const { config: fundAppealConfig } = usePrepareDisputeKitClassicFundAppeal({ + const { config: fundAppealConfig, isError } = usePrepareDisputeKitClassicFundAppeal({ enabled: !isUndefined(id) && !isUndefined(selectedOption), args: [BigInt(id ?? 0), BigInt(selectedOption ?? 0)], value: parsedAmount, @@ -65,7 +65,7 @@ const useFundAppeal = (parsedAmount) => { const { writeAsync: fundAppeal } = useDisputeKitClassicFundAppeal(fundAppealConfig); - return fundAppeal; + return { fundAppeal, isError }; }; interface IFund { @@ -89,39 +89,44 @@ const Fund: React.FC = ({ amount, setAmount, setIsOpen }) => { const parsedAmount = useParsedAmount(debouncedAmount); const [isSending, setIsSending] = useState(false); - const fundAppeal = useFundAppeal(parsedAmount); + const { fundAppeal, isError } = useFundAppeal(parsedAmount); + + const isFundDisabled = useMemo( + () => + isDisconnected || isSending || !balance || parsedAmount > balance.value || Number(parsedAmount) <= 0 || isError, + [isDisconnected, isSending, balance, parsedAmount, isError] + ); return needFund ? ( - -
- { - setAmount(e.target.value); + How much ETH do you want to contribute? + { + setAmount(e.target.value); + }} + placeholder="Amount to fund" + /> + + { + if (fundAppeal) { + setIsSending(true); + wrapWithToast(async () => await fundAppeal().then((response) => response.hash), publicClient) + .then((res) => { + res.status && setIsOpen(true); + }) + .finally(() => { + setIsSending(false); + }); + } }} - placeholder="Amount to fund" /> - - balance.value} - text={isDisconnected ? "Connect to Fund" : "Fund"} - onClick={() => { - if (fundAppeal) { - setIsSending(true); - wrapWithToast(async () => await fundAppeal().then((response) => response.hash), publicClient) - .then(() => { - setIsOpen(true); - }) - .finally(() => { - setIsSending(false); - }); - } - }} - /> - -
+
) : ( <> diff --git a/web/src/pages/Cases/CaseDetails/Appeal/Classic/Options/StageOne.tsx b/web/src/pages/Cases/CaseDetails/Appeal/Classic/Options/StageOne.tsx index 5072dd81b..edd75f7c3 100644 --- a/web/src/pages/Cases/CaseDetails/Appeal/Classic/Options/StageOne.tsx +++ b/web/src/pages/Cases/CaseDetails/Appeal/Classic/Options/StageOne.tsx @@ -3,8 +3,8 @@ import styled from "styled-components"; import StageExplainer from "../StageExplainer"; import OptionCard from "../../OptionCard"; import { + useCountdownContext, useFundingContext, - useLoserSideCountdownContext, useOptionsContext, useSelectedOptionContext, } from "hooks/useClassicAppealContext"; @@ -27,14 +27,14 @@ interface IStageOne { } const StageOne: React.FC = ({ setAmount }) => { - const { paidFees, winningChoice, loserRequiredFunding, winnerRequiredFunding } = useFundingContext(); + const { paidFees, winningChoice, loserRequiredFunding, winnerRequiredFunding, fundedChoices } = useFundingContext(); const options = useOptionsContext(); - const loserSideCountdown = useLoserSideCountdownContext(); + const { loserSideCountdown } = useCountdownContext(); const { selectedOption, setSelectedOption } = useSelectedOptionContext(); return ( - + {!isUndefined(paidFees) && @@ -50,6 +50,7 @@ const StageOne: React.FC = ({ setAmount }) => { winner={i.toString() === winningChoice} funding={paidFees[i] ? BigInt(paidFees[i]) : 0n} required={requiredFunding} + canBeSelected={!fundedChoices?.includes(i.toString())} onClick={() => { setSelectedOption(i); setAmount(formatUnitsWei(requiredFunding)); diff --git a/web/src/pages/Cases/CaseDetails/Appeal/Classic/Options/StageTwo.tsx b/web/src/pages/Cases/CaseDetails/Appeal/Classic/Options/StageTwo.tsx index fe7a4ec66..ff2d16d7b 100644 --- a/web/src/pages/Cases/CaseDetails/Appeal/Classic/Options/StageTwo.tsx +++ b/web/src/pages/Cases/CaseDetails/Appeal/Classic/Options/StageTwo.tsx @@ -1,9 +1,16 @@ import React, { useEffect } from "react"; import styled from "styled-components"; import OptionCard from "../../OptionCard"; -import { useFundingContext, useOptionsContext, useSelectedOptionContext } from "hooks/useClassicAppealContext"; +import { + useCountdownContext, + useFundingContext, + useOptionsContext, + useSelectedOptionContext, +} from "hooks/useClassicAppealContext"; import { isUndefined } from "utils/index"; import { formatUnitsWei } from "utils/format"; +import StageExplainer from "../StageExplainer"; +import Skeleton from "react-loading-skeleton"; const Container = styled.div` margin: 24px 0; @@ -22,35 +29,39 @@ interface IStageTwo { const StageTwo: React.FC = ({ setAmount }) => { const { paidFees, winningChoice, winnerRequiredFunding, fundedChoices } = useFundingContext(); + const { winnerSideCountdown } = useCountdownContext(); const options = useOptionsContext(); const { selectedOption, setSelectedOption } = useSelectedOptionContext(); useEffect(() => { if (!isUndefined(winningChoice)) setSelectedOption(parseInt(winningChoice)); if (!isUndefined(winnerRequiredFunding)) setAmount(formatUnitsWei(winnerRequiredFunding)); - }); + }, [winnerRequiredFunding, winningChoice]); + return ( - {!isUndefined(winningChoice) && - !isUndefined(fundedChoices) && - !isUndefined(paidFees) && - fundedChoices.length > 0 && - !fundedChoices.includes(winningChoice) ? ( + {!isUndefined(winningChoice) && !isUndefined(fundedChoices) && !isUndefined(paidFees) ? ( <> - - - setSelectedOption(parseInt(winningChoice!, 10))} - /> - + {fundedChoices.length > 0 && !fundedChoices.includes(winningChoice) ? ( + <> + + + setSelectedOption(parseInt(winningChoice!, 10))} + /> + + + ) : ( + + )} ) : ( - + )} ); diff --git a/web/src/pages/Cases/CaseDetails/Appeal/Classic/Options/index.tsx b/web/src/pages/Cases/CaseDetails/Appeal/Classic/Options/index.tsx index 95d81fc87..c846ff3d9 100644 --- a/web/src/pages/Cases/CaseDetails/Appeal/Classic/Options/index.tsx +++ b/web/src/pages/Cases/CaseDetails/Appeal/Classic/Options/index.tsx @@ -1,6 +1,6 @@ import React from "react"; import styled from "styled-components"; -import { useLoserSideCountdownContext } from "hooks/useClassicAppealContext"; +import { useCountdownContext } from "hooks/useClassicAppealContext"; import { StyledSkeleton } from "components/StyledSkeleton"; import StageOne from "./StageOne"; import StageTwo from "./StageTwo"; @@ -15,7 +15,7 @@ interface IOptions { } const Options: React.FC = ({ setAmount }) => { - const loserSideCountdown = useLoserSideCountdownContext(); + const { loserSideCountdown } = useCountdownContext(); return !isUndefined(loserSideCountdown) ? ( {loserSideCountdown > 0 ? : } diff --git a/web/src/pages/Cases/CaseDetails/Appeal/Classic/StageExplainer.tsx b/web/src/pages/Cases/CaseDetails/Appeal/Classic/StageExplainer.tsx index d3fd96015..206ce2d79 100644 --- a/web/src/pages/Cases/CaseDetails/Appeal/Classic/StageExplainer.tsx +++ b/web/src/pages/Cases/CaseDetails/Appeal/Classic/StageExplainer.tsx @@ -3,6 +3,9 @@ import styled from "styled-components"; import { Box } from "@kleros/ui-components-library"; import { secondsToDayHourMinute } from "utils/date"; import HourglassIcon from "svgs/icons/hourglass.svg"; +import { isUndefined } from "utils/index"; +import { useFundingContext, useOptionsContext } from "hooks/useClassicAppealContext"; +import Skeleton from "react-loading-skeleton"; const StyledBox = styled(Box)` border-radius: 3px; @@ -12,6 +15,7 @@ const StyledBox = styled(Box)` padding: 16px 24px; & > div > label { display: block; + margin-bottom: 4px; } `; @@ -31,24 +35,63 @@ const CountdownLabel = styled.label` `; interface IStageExplainer { - loserSideCountdown: number | undefined; + countdown: number | undefined; + stage: 1 | 2; } -const StageExplainer: React.FC = ({ loserSideCountdown }) => ( - - - - {typeof loserSideCountdown !== "undefined" && secondsToDayHourMinute(loserSideCountdown)} - +const StageOneExplanation: React.FC = () => ( +
+ {" "} + + +
+); + +const StageTwoExplanation: React.FC = () => { + const { fundedChoices } = useFundingContext(); + const options = useOptionsContext(); + return (
+
-
-); + ); +}; + +const StageExplainer: React.FC = ({ countdown, stage }) => { + return ( + + + {!isUndefined(countdown) ? ( + <> + + {countdown > 0 ? secondsToDayHourMinute(countdown) : Time's up} + + ) : null} + + {stage === 1 ? : } + + ); +}; export default StageExplainer; diff --git a/web/src/pages/Cases/CaseDetails/Overview/index.tsx b/web/src/pages/Cases/CaseDetails/Overview/index.tsx index 84f22374b..bfa74cbf8 100644 --- a/web/src/pages/Cases/CaseDetails/Overview/index.tsx +++ b/web/src/pages/Cases/CaseDetails/Overview/index.tsx @@ -11,8 +11,8 @@ import DisputeInfo from "components/DisputeCard/DisputeInfo"; import Verdict from "components/Verdict/index"; import { useVotingHistory } from "hooks/queries/useVotingHistory"; import { getLocalRounds } from "utils/getLocalRounds"; -import { DisputeContext } from "./DisputeContext"; -import { Policies } from "./Policies"; +import { DisputeContext } from "components/DisputePreview/DisputeContext"; +import { Policies } from "components/DisputePreview/Policies"; import { responsiveSize } from "styles/responsiveSize"; const Container = styled.div` diff --git a/web/src/pages/Cases/CaseDetails/Tabs.tsx b/web/src/pages/Cases/CaseDetails/Tabs.tsx index 1321d675e..21ff58fd1 100644 --- a/web/src/pages/Cases/CaseDetails/Tabs.tsx +++ b/web/src/pages/Cases/CaseDetails/Tabs.tsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect } from "react"; +import React, { useState, useEffect, useMemo } from "react"; import styled from "styled-components"; import { useNavigate, useLocation, useParams } from "react-router-dom"; import { Tabs as TabsComponent } from "@kleros/ui-components-library"; @@ -67,16 +67,19 @@ const Tabs: React.FC = () => { setCurrentTab(TABS.findIndex(({ path }) => path === currentPathName)); }, [currentPathName]); - useEffect(() => { - TABS[3].disabled = + const tabs = useMemo(() => { + const updatedTabs = [...TABS]; + updatedTabs[3].disabled = (parseInt(currentPeriodIndex) < 3 && rounds.length === 1) || (!isUndefined(appealCost) && isLastRound(appealCost) && parseInt(currentPeriodIndex) === 3); - }, [currentPeriodIndex, id, currentTab, rounds.length, appealCost]); + + return updatedTabs; + }, [currentPeriodIndex, id, rounds.length, appealCost]); return ( { setCurrentTab(n); navigate(TABS[n].path); diff --git a/web/src/pages/Cases/CaseDetails/Timeline.tsx b/web/src/pages/Cases/CaseDetails/Timeline.tsx index 58e703ae0..cf54a6e4a 100644 --- a/web/src/pages/Cases/CaseDetails/Timeline.tsx +++ b/web/src/pages/Cases/CaseDetails/Timeline.tsx @@ -1,4 +1,4 @@ -import React from "react"; +import React, { useMemo } from "react"; import styled, { css } from "styled-components"; import { landscapeStyle } from "styles/landscapeStyle"; import { Periods } from "consts/periods"; @@ -6,6 +6,7 @@ import { DisputeDetailsQuery } from "queries/useDisputeDetailsQuery"; import { Box, Steps } from "@kleros/ui-components-library"; import { StyledSkeleton } from "components/StyledSkeleton"; import { useCountdown } from "hooks/useCountdown"; +import useIsDesktop from "hooks/useIsDesktop"; import { secondsToDayHourMinute } from "utils/date"; import { responsiveSize } from "styles/responsiveSize"; @@ -34,29 +35,11 @@ const StyledSteps = styled(Steps)` margin: auto; `; -const TitleMobile = styled.span` - ${landscapeStyle( - () => css` - display: none; - ` - )} -`; - -const TitleDesktop = styled(TitleMobile)` - display: none; - - ${landscapeStyle( - () => css` - display: inline-block; - ` - )} -`; - const Timeline: React.FC<{ dispute: DisputeDetailsQuery["dispute"]; currentPeriodIndex: number; }> = ({ currentPeriodIndex, dispute }) => { - const currentItemIndex = currentPeriodToCurrentItem(currentPeriodIndex, dispute?.ruled); + const currentItemIndex = currentPeriodToCurrentItem(currentPeriodIndex, dispute?.court.hiddenVotes); const items = useTimeline(dispute, currentItemIndex, currentItemIndex); return ( @@ -65,19 +48,21 @@ const Timeline: React.FC<{ ); }; -const currentPeriodToCurrentItem = (currentPeriodIndex: number, ruled?: boolean): number => { +const currentPeriodToCurrentItem = (currentPeriodIndex: number, hiddenVotes?: boolean): number => { + if (hiddenVotes) return currentPeriodIndex; if (currentPeriodIndex <= Periods.commit) return currentPeriodIndex; - else if (currentPeriodIndex < Periods.execution) return currentPeriodIndex - 1; - else return ruled ? 5 : currentPeriodIndex - 1; + else return currentPeriodIndex - 1; }; const useTimeline = (dispute: DisputeDetailsQuery["dispute"], currentItemIndex: number, currentPeriodIndex: number) => { - const titles = [ - { mobile: "Evidence", desktop: "Evidence Period" }, - { mobile: "Voting", desktop: "Voting Period" }, - { mobile: "Appeal", desktop: "Appeal Period" }, - { mobile: "Executed", desktop: "Executed" }, - ]; + const isDesktop = useIsDesktop(); + const titles = useMemo(() => { + const titles = ["Evidence", "Voting", "Appeal", "Executed"]; + if (dispute?.court.hiddenVotes) { + titles.splice(1, 0, "Commit"); + } + return titles; + }, [dispute]); const deadlineCurrentPeriod = getDeadline( currentPeriodIndex, dispute?.lastPeriodChange, @@ -86,12 +71,12 @@ const useTimeline = (dispute: DisputeDetailsQuery["dispute"], currentItemIndex: const countdown = useCountdown(deadlineCurrentPeriod); const getSubitems = (index: number): string[] | React.ReactNode[] => { if (typeof countdown !== "undefined" && dispute) { - if (index === currentItemIndex && countdown === 0) { + if (index === titles.length - 1) { + return []; + } else if (index === currentItemIndex && countdown === 0) { return ["Time's up!"]; } else if (index < currentItemIndex) { return []; - } else if (index === 3) { - return currentItemIndex === 3 ? ["Pending"] : []; } else if (index === currentItemIndex) { return [secondsToDayHourMinute(countdown)]; } else { @@ -101,12 +86,7 @@ const useTimeline = (dispute: DisputeDetailsQuery["dispute"], currentItemIndex: return []; }; return titles.map((title, i) => ({ - title: ( - <> - {title.mobile} - {title.desktop} - - ), + title: i + 1 < titles.length && isDesktop ? `${title} Period` : title, subitems: getSubitems(i), })); }; diff --git a/web/src/pages/Cases/CaseDetails/Voting/Classic.tsx b/web/src/pages/Cases/CaseDetails/Voting/Classic.tsx deleted file mode 100644 index c26588ddf..000000000 --- a/web/src/pages/Cases/CaseDetails/Voting/Classic.tsx +++ /dev/null @@ -1,136 +0,0 @@ -import React, { useMemo, useState } from "react"; -import styled from "styled-components"; -import { useParams } from "react-router-dom"; -import { useWalletClient, usePublicClient } from "wagmi"; -import { Button, Textarea } from "@kleros/ui-components-library"; -import { prepareWriteDisputeKitClassic } from "hooks/contracts/generated"; -import { wrapWithToast } from "utils/wrapWithToast"; -import { useDisputeTemplate } from "queries/useDisputeTemplate"; -import { useDisputeDetailsQuery } from "queries/useDisputeDetailsQuery"; -import { EnsureChain } from "components/EnsureChain"; -import ReactMarkdown from "react-markdown"; - -const Container = styled.div` - width: 100%; - height: auto; -`; -const MainContainer = styled.div` - width: 100%; - height: auto; -`; -const StyledTextarea = styled(Textarea)` - width: 100%; - height: auto; - textarea { - height: 200px; - border-color: ${({ theme }) => theme.stroke}; - } - small { - font-weight: 400; - hyphens: auto; - } -`; -const OptionsContainer = styled.div` - margin-top: 24px; - display: flex; - flex-wrap: wrap; - justify-content: center; - gap: 16px; -`; -const RefuseToArbitrateContainer = styled.div` - width: 100%; - background-color: ${({ theme }) => theme.lightBlue}; - padding: 32px; - display: flex; - justify-content: center; -`; - -interface IClassic { - arbitrable: `0x${string}`; - voteIDs: string[]; - setIsOpen: (val: boolean) => void; -} - -const Classic: React.FC = ({ arbitrable, voteIDs, setIsOpen }) => { - const { id } = useParams(); - const parsedDisputeID = BigInt(id ?? 0); - const parsedVoteIDs = useMemo(() => voteIDs.map((voteID) => BigInt(voteID)), [voteIDs]); - const { data: disputeTemplate } = useDisputeTemplate(id, arbitrable); - const { data: disputeData } = useDisputeDetailsQuery(id); - const [chosenOption, setChosenOption] = useState(-1); - const [isSending, setIsSending] = useState(false); - const [justification, setJustification] = useState(""); - const { data: walletClient } = useWalletClient(); - const publicClient = usePublicClient(); - - const handleVote = async (voteOption: number) => { - setIsSending(true); - setChosenOption(voteOption); - const { request } = await prepareWriteDisputeKitClassic({ - functionName: "castVote", - args: [ - parsedDisputeID, - parsedVoteIDs, - BigInt(voteOption), - BigInt(disputeData?.dispute?.currentRoundIndex), - justification, - ], - }); - if (walletClient) { - wrapWithToast(async () => await walletClient.writeContract(request), publicClient) - .then(() => { - setIsOpen(true); - }) - .finally(() => { - setChosenOption(-1); - setIsSending(false); - }); - } - }; - - return id ? ( - - - {disputeTemplate?.question} - setJustification(e.target.value)} - placeholder="Justify your vote..." - message={ - "A good justification contributes to case comprehension. " + "Low quality justifications can be challenged." - } - variant="info" - /> - - {disputeTemplate?.answers?.map((answer: { title: string; description: string }, i: number) => { - return ( - -