diff --git a/commons/abis.js b/commons/abis.js index 03a4fc3f..c3202f66 100644 --- a/commons/abis.js +++ b/commons/abis.js @@ -13,6 +13,7 @@ const REWARDABLE_VALIDATORS_ABI = require('../contracts/build/contracts/Rewardab const HOME_AMB_ABI = require('../contracts/build/contracts/HomeAMB').abi const FOREIGN_AMB_ABI = require('../contracts/build/contracts/ForeignAMB').abi const BOX_ABI = require('../contracts/build/contracts/Box').abi +const SAI_TOP = require('../contracts/build/contracts/SaiTopMock').abi const { HOME_V1_ABI, FOREIGN_V1_ABI } = require('./v1Abis') const { BRIDGE_MODES } = require('./constants') @@ -92,5 +93,6 @@ module.exports = { ERC20_BYTES32_ABI, HOME_AMB_ABI, FOREIGN_AMB_ABI, - BOX_ABI + BOX_ABI, + SAI_TOP } diff --git a/contracts b/contracts index 20d26270..055bcb62 160000 --- a/contracts +++ b/contracts @@ -1 +1 @@ -Subproject commit 20d262702defd3aed39870f3379f1cf0c314305e +Subproject commit 055bcb62a794ed913dfec10b6558e3b57a314151 diff --git a/deployment-e2e/molecule/ultimate-commons/oracle-add-docker-external-network.yml b/deployment-e2e/molecule/ultimate-commons/oracle-add-docker-external-network.yml new file mode 100644 index 00000000..71bd9e07 --- /dev/null +++ b/deployment-e2e/molecule/ultimate-commons/oracle-add-docker-external-network.yml @@ -0,0 +1,26 @@ +--- +- name: Slurp docker compose file + slurp: + src: "/home/poadocker/bridge/oracle/{{ item }}.yml" + register: docker_compose_slurp +- name: Parse docker compose file + set_fact: + docker_compose_parsed: "{{ docker_compose_slurp['content'] | b64decode | from_yaml }}" + +- name: Add the external network used to connect to Parity nodes + set_fact: + docker_compose_parsed: "{{ docker_compose_parsed |combine({'networks': {'ultimate': {'external': 'true'}}}, recursive=True) }}" + +- name: Add all Oracle containers to the network + set_fact: + docker_compose_parsed: "{{ docker_compose_parsed | combine({'services': {item: {'networks': docker_compose_parsed.services[item].networks | union(['ultimate'])}}}, recursive=True) }}" + with_items: "{{ docker_compose_parsed.services }}" + +- name: Expose Redis port to allow connecting from redis-cli + set_fact: + docker_compose_parsed: "{{ docker_compose_parsed | combine({'services': {'redis': {'ports': ['6379:6379']}}}, recursive=True) }}" + +- name: Write updated docker file + copy: + content: "{{ docker_compose_parsed | to_yaml }}" + dest: "/home/poadocker/bridge/oracle/{{ item }}.yml" diff --git a/deployment-e2e/molecule/ultimate-commons/oracle-docker-compose.yml b/deployment-e2e/molecule/ultimate-commons/oracle-docker-compose.yml index cbef4865..98661d6a 100644 --- a/deployment-e2e/molecule/ultimate-commons/oracle-docker-compose.yml +++ b/deployment-e2e/molecule/ultimate-commons/oracle-docker-compose.yml @@ -5,58 +5,12 @@ tasks: - name: stop the service shell: service poabridge stop - - - name: Slurp docker compose file - slurp: - src: "/home/poadocker/bridge/oracle/docker-compose.yml" - register: docker_compose_slurp - - name: Parse docker compose file - set_fact: - docker_compose_parsed: "{{ docker_compose_slurp['content'] | b64decode | from_yaml }}" - - name: Add the external network used to connect to Parity nodes - set_fact: - docker_compose_parsed: "{{ docker_compose_parsed |combine({'networks': {'ultimate': {'external': 'true'}}}, recursive=True) }}" - - - name: Add all Oracle containers to the network - set_fact: - docker_compose_parsed: "{{ docker_compose_parsed | combine({'services': {item: {'networks': docker_compose_parsed.services[item].networks | union(['ultimate'])}}}, recursive=True) }}" - with_items: "{{ docker_compose_parsed.services }}" - - - name: Expose Redis port to allow connecting from redis-cli - set_fact: - docker_compose_parsed: "{{ docker_compose_parsed | combine({'services': {'redis': {'ports': ['6379:6379']}}}, recursive=True) }}" - - - name: Write new docker-compose file - copy: - content: "{{ docker_compose_parsed | to_yaml }}" - dest: "/home/poadocker/bridge/oracle/docker-compose.yml" - - - name: Slurp docker compose extended file - slurp: - src: "/home/poadocker/bridge/oracle/docker-compose-transfer.yml" - register: docker_compose_extended_slurp - - name: Parse docker compose file - set_fact: - docker_compose_extended_parsed: "{{ docker_compose_extended_slurp['content'] | b64decode | from_yaml }}" - - - name: Add the external network used to connect to Parity nodes in compose extended file - set_fact: - docker_compose_extended_parsed: "{{ docker_compose_extended_parsed |combine({'networks': {'ultimate': {'external': 'true'}}}, recursive=True) }}" - - - name: Add all Oracle containers to the network in compose extended file - set_fact: - docker_compose_extended_parsed: "{{ docker_compose_extended_parsed | combine({'services': {item: {'networks': docker_compose_extended_parsed.services[item].networks | union(['ultimate'])}}}, recursive=True) }}" - with_items: "{{ docker_compose_extended_parsed.services }}" - - - name: Expose Redis port to allow connecting from redis-cli in compose extended file - set_fact: - docker_compose_extended_parsed: "{{ docker_compose_extended_parsed | combine({'services': {'redis': {'ports': ['6379:6379']}}}, recursive=True) }}" - - - name: Write new docker-compose extended file - copy: - content: "{{ docker_compose_extended_parsed | to_yaml }}" - dest: "/home/poadocker/bridge/oracle/docker-compose-transfer.yml" + - include_tasks: oracle-add-docker-external-network.yml + with_items: + - docker-compose + - docker-compose-transfer + - docker-compose-erc-native - name: start the service shell: service poabridge start diff --git a/deployment/roles/oracle/tasks/logging.yml b/deployment/roles/oracle/tasks/logging.yml index 04412d17..60011f37 100644 --- a/deployment/roles/oracle/tasks/logging.yml +++ b/deployment/roles/oracle/tasks/logging.yml @@ -1,41 +1,9 @@ --- -- name: Slurp docker compose file - slurp: - src: "{{ bridge_path }}/oracle/docker-compose.yml" - register: docker_compose_slurp - -- name: Parse docker compose file - set_fact: - docker_compose_parsed: "{{ docker_compose_slurp['content'] | b64decode | from_yaml }}" - -- name: Set logger to remote server - set_fact: - docker_compose_parsed: "{{ docker_compose_parsed |combine({'services': {item: {'logging': {'driver': 'syslog','options': {'tag': '{{.Name}}/{{.ID}}'}}}}}, recursive=True) }}" - with_items: "{{ docker_compose_parsed.services }}" - -- name: Write new docker-compose file - copy: - content: "{{ docker_compose_parsed | to_yaml }}" - dest: "{{ bridge_path }}/oracle/docker-compose.yml" - -- name: Slurp docker compose extended file - slurp: - src: "{{ bridge_path }}/oracle/docker-compose-transfer.yml" - register: docker_compose_extended_slurp - -- name: Parse docker compose extended file - set_fact: - docker_compose_extended_parsed: "{{ docker_compose_extended_slurp['content'] | b64decode | from_yaml }}" - -- name: Set logger to remote server for extended file - set_fact: - docker_compose_extended_parsed: "{{ docker_compose_extended_parsed |combine({'services': {item: {'logging': {'driver': 'syslog','options': {'tag': '{{.Name}}/{{.ID}}'}}}}}, recursive=True) }}" - with_items: "{{ docker_compose_extended_parsed.services }}" - -- name: Write new docker-compose-extended file - copy: - content: "{{ docker_compose_extended_parsed | to_yaml }}" - dest: "{{ bridge_path }}/oracle/docker-compose-transfer.yml" +- include_tasks: logging_by_syslog.yml + with_items: + - docker-compose + - docker-compose-transfer + - docker-compose-erc-native - name: Set the local container logs configuration file template: diff --git a/deployment/roles/oracle/tasks/logging_by_syslog.yml b/deployment/roles/oracle/tasks/logging_by_syslog.yml new file mode 100644 index 00000000..4ac50828 --- /dev/null +++ b/deployment/roles/oracle/tasks/logging_by_syslog.yml @@ -0,0 +1,19 @@ +--- +- name: Slurp docker compose file + slurp: + src: "{{ bridge_path }}/oracle/{{ item }}.yml" + register: docker_compose_slurp + +- name: Parse docker compose file + set_fact: + docker_compose_parsed: "{{ docker_compose_slurp['content'] | b64decode | from_yaml }}" + +- name: Set logger to remote server + set_fact: + docker_compose_parsed: "{{ docker_compose_parsed |combine({'services': {item: {'logging': {'driver': 'syslog','options': {'tag': '{{.Name}}/{{.ID}}'}}}}}, recursive=True) }}" + with_items: "{{ docker_compose_parsed.services }}" + +- name: Write updated docker file + copy: + content: "{{ docker_compose_parsed | to_yaml }}" + dest: "{{ bridge_path }}/oracle/{{ item }}.yml" diff --git a/deployment/roles/oracle/tasks/post_config.yml b/deployment/roles/oracle/tasks/post_config.yml index f4b635fe..4eab39d8 100644 --- a/deployment/roles/oracle/tasks/post_config.yml +++ b/deployment/roles/oracle/tasks/post_config.yml @@ -40,7 +40,11 @@ - name: Extend docker compose file set_fact: composefileoverride="-f docker-compose-transfer.yml" - when: ORACLE_BRIDGE_MODE == "ERC_TO_NATIVE" or ( ORACLE_BRIDGE_MODE == "ERC_TO_ERC" and FOREIGN_ERC_TYPE != "ERC677") + when: ORACLE_BRIDGE_MODE == "ERC_TO_ERC" and FOREIGN_ERC_TYPE != "ERC677" + +- name: Extend docker compose file for erc to native + set_fact: composefileoverride="-f docker-compose-erc-native.yml" + when: ORACLE_BRIDGE_MODE == "ERC_TO_NATIVE" - name: Install .key config template: diff --git a/e2e-commons/constants.json b/e2e-commons/constants.json index cd63c80d..8929f452 100644 --- a/e2e-commons/constants.json +++ b/e2e-commons/constants.json @@ -33,7 +33,9 @@ "ercToNativeBridge": { "home": "0x488Af810997eD1730cB3a3918cD83b3216E6eAda", "foreign": "0x488Af810997eD1730cB3a3918cD83b3216E6eAda", - "foreignToken": "0x3C665A31199694Bf723fD08844AD290207B5797f", + "foreignToken": "0x7cc4b1851c35959d34e635a470f6b5c43ba3c9c9", + "halfDuplexToken": "0x89d24A6b4CcB1B6fAA2625fE562bDD9a23260359", + "saiTop": "0x9b0ccf7C8994E19F39b2B4CF708e0A7DF65fA8a3", "ui": "http://localhost:3002", "monitor": "http://monitor-erc20-native:3012" }, diff --git a/e2e-commons/contracts-envs/erc-to-native.env b/e2e-commons/contracts-envs/erc-to-native.env index 7797aa85..f7991673 100644 --- a/e2e-commons/contracts-envs/erc-to-native.env +++ b/e2e-commons/contracts-envs/erc-to-native.env @@ -32,7 +32,7 @@ FOREIGN_GAS_PRICE=10000000000 FOREIGN_REWARDABLE=false BLOCK_REWARD_ADDRESS=0xF9698Eb93702dfdd0e2d802088d4c21822a8A977 -ERC20_TOKEN_ADDRESS=0x3C665A31199694Bf723fD08844AD290207B5797f +ERC20_TOKEN_ADDRESS=0x89d24A6b4CcB1B6fAA2625fE562bDD9a23260359 REQUIRED_NUMBER_OF_VALIDATORS=1 VALIDATORS="0xaaB52d66283F7A1D5978bcFcB55721ACB467384b" diff --git a/e2e-commons/up.sh b/e2e-commons/up.sh index 86563fc8..5a8053b3 100755 --- a/e2e-commons/up.sh +++ b/e2e-commons/up.sh @@ -22,6 +22,8 @@ while [ "$1" != "" ]; do docker-compose run -d oracle-erc20-native yarn watcher:collected-signatures docker-compose run -d oracle-erc20-native yarn watcher:affirmation-request docker-compose run -d oracle-erc20-native yarn watcher:transfer + docker-compose run -d oracle-erc20-native yarn watcher:half-duplex-transfer + docker-compose run -d oracle-erc20-native yarn worker:swap-tokens docker-compose run -d oracle-amb yarn watcher:signature-request docker-compose run -d oracle-amb yarn watcher:collected-signatures docker-compose run -d oracle-amb yarn watcher:affirmation-request diff --git a/monitor-e2e/test/ercToNative.js b/monitor-e2e/test/ercToNative.js index 61a996a8..b7c2b568 100644 --- a/monitor-e2e/test/ercToNative.js +++ b/monitor-e2e/test/ercToNative.js @@ -25,7 +25,7 @@ describe('ERC TO NATIVE with changing state of contracts', () => { }) it('should change balanceDiff', async () => { - await sendTokens(foreignRPC.URL, user, ercToNativeBridge.foreignToken, ercToNativeBridge.foreign) + await sendTokens(foreignRPC.URL, user, ercToNativeBridge.halfDuplexToken, ercToNativeBridge.foreign) await waitUntil(async () => { ;({ data } = await axios.get(`${baseUrl}`)) diff --git a/oracle-e2e/test/ercToNative.js b/oracle-e2e/test/ercToNative.js index dd548322..ba474693 100644 --- a/oracle-e2e/test/ercToNative.js +++ b/oracle-e2e/test/ercToNative.js @@ -1,8 +1,15 @@ const Web3 = require('web3') const assert = require('assert') const promiseRetry = require('promise-retry') -const { user, secondUser, ercToNativeBridge, homeRPC, foreignRPC } = require('../../e2e-commons/constants.json') -const { ERC677_BRIDGE_TOKEN_ABI, FOREIGN_ERC_TO_NATIVE_ABI } = require('../../commons') +const { + user, + secondUser, + validator, + ercToNativeBridge, + homeRPC, + foreignRPC +} = require('../../e2e-commons/constants.json') +const { ERC677_BRIDGE_TOKEN_ABI, FOREIGN_ERC_TO_NATIVE_ABI, SAI_TOP } = require('../../commons') const { generateNewBlock } = require('../../e2e-commons/utils') const homeWeb3 = new Web3(new Web3.providers.HttpProvider(homeRPC.URL)) @@ -15,11 +22,134 @@ const { toBN } = foreignWeb3.utils homeWeb3.eth.accounts.wallet.add(user.privateKey) foreignWeb3.eth.accounts.wallet.add(user.privateKey) +foreignWeb3.eth.accounts.wallet.add(validator.privateKey) const erc20Token = new foreignWeb3.eth.Contract(ERC677_BRIDGE_TOKEN_ABI, ercToNativeBridge.foreignToken) const foreignBridge = new foreignWeb3.eth.Contract(FOREIGN_ERC_TO_NATIVE_ABI, COMMON_FOREIGN_BRIDGE_ADDRESS) describe('erc to native', () => { + let halfDuplexTokenAddress + let halfDuplexToken + before(async () => { + halfDuplexTokenAddress = await foreignBridge.methods.halfDuplexErc20token().call() + halfDuplexToken = new foreignWeb3.eth.Contract(ERC677_BRIDGE_TOKEN_ABI, halfDuplexTokenAddress) + }) + it('should continue working after migration', async () => { + const originalBalanceOnHome = await homeWeb3.eth.getBalance(user.address) + + const transferValue = homeWeb3.utils.toWei('0.01') + + // erc20 token address and half duplex address are the same before migration + const tokenAddress = await foreignBridge.methods.erc20token().call() + + const erc20AndhalfDuplexToken = new foreignWeb3.eth.Contract(ERC677_BRIDGE_TOKEN_ABI, tokenAddress) + + // send tokens to foreign bridge + await erc20AndhalfDuplexToken.methods + .transfer(COMMON_FOREIGN_BRIDGE_ADDRESS, transferValue) + .send({ + from: user.address, + gas: '1000000' + }) + .catch(e => { + console.error(e) + }) + + // Send a trivial transaction to generate a new block since the watcher + // is configured to wait 1 confirmation block + await generateNewBlock(foreignWeb3, user.address) + + // check that balance increases + await promiseRetry(async (retry, number) => { + const balance = await homeWeb3.eth.getBalance(user.address) + await generateNewBlock(foreignWeb3, user.address) + // retry at least 4 times to check transfer is not double processed by the two watchers + if (toBN(balance).lte(toBN(originalBalanceOnHome)) || number < 4) { + retry() + } else { + assert( + toBN(balance).eq(toBN(originalBalanceOnHome).add(toBN(transferValue))), + 'User balance should be increased only by second transfer' + ) + } + }) + + // call migration + await foreignBridge.methods.migrateToMCD().send({ + from: validator.address, + gas: '4000000' + }) + + // update min threshold for swap + await foreignBridge.methods.setMinHDTokenBalance(foreignWeb3.utils.toWei('1', 'ether')).send({ + from: validator.address, + gas: '1000000' + }) + + const AfterMigrateBalance = await homeWeb3.eth.getBalance(user.address) + + // send tokens to foreign bridge + await erc20Token.methods + .transfer(COMMON_FOREIGN_BRIDGE_ADDRESS, transferValue) + .send({ + from: user.address, + gas: '1000000' + }) + .catch(e => { + console.error(e) + }) + + // Send a trivial transaction to generate a new block since the watcher + // is configured to wait 1 confirmation block + await generateNewBlock(foreignWeb3, user.address) + + // check that balance increases + await promiseRetry(async (retry, number) => { + const balance = await homeWeb3.eth.getBalance(user.address) + await generateNewBlock(foreignWeb3, user.address) + // retry at least 4 times to check transfer is not double processed by the two watchers + if (toBN(balance).lte(toBN(AfterMigrateBalance)) || number < 4) { + retry() + } else { + assert( + toBN(balance).eq(toBN(AfterMigrateBalance).add(toBN(transferValue))), + 'User balance should be increased only by second transfer' + ) + } + }) + + const afterMigrateAndTransferBalance = await homeWeb3.eth.getBalance(user.address) + + // send tokens to foreign bridge + await halfDuplexToken.methods + .transfer(COMMON_FOREIGN_BRIDGE_ADDRESS, transferValue) + .send({ + from: user.address, + gas: '1000000' + }) + .catch(e => { + console.error(e) + }) + + // Send a trivial transaction to generate a new block since the watcher + // is configured to wait 1 confirmation block + await generateNewBlock(foreignWeb3, user.address) + + // check that balance increases + await promiseRetry(async (retry, number) => { + const balance = await homeWeb3.eth.getBalance(user.address) + await generateNewBlock(foreignWeb3, user.address) + // retry at least 4 times to check transfer is not double processed by the two watchers + if (toBN(balance).lte(toBN(afterMigrateAndTransferBalance)) || number < 4) { + retry() + } else { + assert( + toBN(balance).eq(toBN(afterMigrateAndTransferBalance).add(toBN(transferValue))), + 'User balance should be increased only by second transfer' + ) + } + }) + }) it('should convert tokens in foreign to coins in home', async () => { const balance = await erc20Token.methods.balanceOf(user.address).call() const originalBalanceOnHome = await homeWeb3.eth.getBalance(user.address) @@ -92,6 +222,238 @@ describe('erc to native', () => { } }) }) + it('should convert half duplex token in foreign to native token in home', async () => { + const originalBalanceOnHome = await homeWeb3.eth.getBalance(user.address) + const bridgeErc20TokenBalance = await erc20Token.methods.balanceOf(COMMON_FOREIGN_BRIDGE_ADDRESS).call() + const bridgeHalfDuplexBalance = await halfDuplexToken.methods.balanceOf(COMMON_FOREIGN_BRIDGE_ADDRESS).call() + + const valueToTransfer = foreignWeb3.utils.toWei('1', 'ether') + + // this transfer won't trigger a call to swap tokens + await halfDuplexToken.methods.transfer(COMMON_FOREIGN_BRIDGE_ADDRESS, valueToTransfer).send({ + from: user.address, + gas: '1000000' + }) + + // Send a trivial transaction to generate a new block since the watcher + // is configured to wait 1 confirmation block + await generateNewBlock(foreignWeb3, user.address) + + // check that balance increases + await promiseRetry(async retry => { + const balance = await homeWeb3.eth.getBalance(user.address) + if (toBN(balance).lte(toBN(originalBalanceOnHome))) { + retry() + } else { + assert( + toBN(balance).eq(toBN(originalBalanceOnHome).add(toBN(valueToTransfer))), + 'User balance should be increased by the half duplex token transfer' + ) + } + }) + + const updatedBalanceOnHome = await homeWeb3.eth.getBalance(user.address) + const updatedBridgeHalfDuplexBalance = await halfDuplexToken.methods.balanceOf(COMMON_FOREIGN_BRIDGE_ADDRESS).call() + assert( + toBN(updatedBridgeHalfDuplexBalance).eq(toBN(bridgeHalfDuplexBalance).add(toBN(valueToTransfer))), + 'Bridge balance should reflect the transfer value' + ) + + // this transfer will trigger call to swap tokens + await halfDuplexToken.methods.transfer(COMMON_FOREIGN_BRIDGE_ADDRESS, valueToTransfer).send({ + from: user.address, + gas: '1000000' + }) + + await generateNewBlock(foreignWeb3, user.address) + + await promiseRetry(async retry => { + const userBalance = await homeWeb3.eth.getBalance(user.address) + const updatedBridgeErc20TokenBalance = await erc20Token.methods.balanceOf(COMMON_FOREIGN_BRIDGE_ADDRESS).call() + + if ( + toBN(userBalance).lte(toBN(updatedBalanceOnHome)) || + toBN(updatedBridgeErc20TokenBalance).lte(toBN(bridgeErc20TokenBalance)) + ) { + retry() + } else { + assert( + toBN(userBalance).eq(toBN(updatedBalanceOnHome).add(toBN(valueToTransfer))), + 'User balance should be increased by the half duplex token transfer' + ) + const updatedBalance = await halfDuplexToken.methods.balanceOf(COMMON_FOREIGN_BRIDGE_ADDRESS).call() + assert(toBN(updatedBalance).isZero(), 'Half duplex bridge balance should be zero') + assert( + toBN(updatedBridgeErc20TokenBalance).eq( + toBN(bridgeErc20TokenBalance) + .add(toBN(bridgeHalfDuplexBalance)) + .add(toBN(foreignWeb3.utils.toWei('2', 'ether'))) + ), + 'Erc20 token balance should be correctly increased by the token swap' + ) + } + }) + }) + it('should convert half duplex token in foreign to native token in home for alternative receiver ', async () => { + const originalBalanceOnHome = await homeWeb3.eth.getBalance(user.address) + const initialBalanceSecondUser = await homeWeb3.eth.getBalance(secondUser.address) + const bridgeErc20TokenBalance = await erc20Token.methods.balanceOf(COMMON_FOREIGN_BRIDGE_ADDRESS).call() + + const valueToTransfer = foreignWeb3.utils.toWei('1', 'ether') + + // approve tokens to foreign bridge + await halfDuplexToken.methods + .approve(COMMON_FOREIGN_BRIDGE_ADDRESS, valueToTransfer) + .send({ + from: user.address, + gas: '1000000' + }) + .catch(e => { + console.error(e) + }) + + // call bridge method to transfer tokens to a different recipient + await foreignBridge.methods['relayTokens(address,uint256,address)']( + secondUser.address, + valueToTransfer, + halfDuplexTokenAddress + ) + .send({ + from: user.address, + gas: '1000000' + }) + .catch(e => { + console.error(e) + }) + + // Send a trivial transaction to generate a new block since the watcher + // is configured to wait 1 confirmation block + await generateNewBlock(foreignWeb3, user.address) + + // check that balance increases + await promiseRetry(async retry => { + const secondUserbalance = await homeWeb3.eth.getBalance(secondUser.address) + const updatedBridgeErc20TokenBalance = await erc20Token.methods.balanceOf(COMMON_FOREIGN_BRIDGE_ADDRESS).call() + const userbalance = await homeWeb3.eth.getBalance(user.address) + assert(toBN(userbalance).lte(toBN(originalBalanceOnHome)), 'User balance should be the same') + if ( + toBN(secondUserbalance).lte(toBN(initialBalanceSecondUser)) || + toBN(updatedBridgeErc20TokenBalance).lte(toBN(bridgeErc20TokenBalance)) + ) { + retry() + } else { + assert( + toBN(secondUserbalance).eq(toBN(initialBalanceSecondUser).add(toBN(valueToTransfer))), + 'User balance should be increased by the half duplex token transfer' + ) + const updatedHDBalance = await halfDuplexToken.methods.balanceOf(COMMON_FOREIGN_BRIDGE_ADDRESS).call() + assert(toBN(updatedHDBalance).isZero(), 'Half duplex bridge balance should be zero') + assert( + toBN(updatedBridgeErc20TokenBalance).eq(toBN(bridgeErc20TokenBalance).add(toBN(valueToTransfer))), + 'Erc20 token balance should be correctly increased by the token swap' + ) + } + }) + }) + it('should not relay half duplex token transfer after Emergency Shutdown', async () => { + const originalBalanceOnHome = await homeWeb3.eth.getBalance(user.address) + const bridgeErc20TokenBalance = await erc20Token.methods.balanceOf(COMMON_FOREIGN_BRIDGE_ADDRESS).call() + const bridgeHalfDuplexBalance = await halfDuplexToken.methods.balanceOf(COMMON_FOREIGN_BRIDGE_ADDRESS).call() + + const block = await foreignWeb3.eth.getBlock('latest') + const saiTop = new foreignWeb3.eth.Contract(SAI_TOP, ercToNativeBridge.saiTop) + + // Trigger Emergency Shutdown + await saiTop.methods + .setCaged(block.timestamp) + .send({ + from: user.address, + gas: '1000000' + }) + .catch(e => { + console.error(e) + }) + + const valueToTransfer = foreignWeb3.utils.toWei('1', 'ether') + + await generateNewBlock(foreignWeb3, user.address) + + // this transfer won't trigger a call to swap tokens + await halfDuplexToken.methods.transfer(COMMON_FOREIGN_BRIDGE_ADDRESS, valueToTransfer).send({ + from: user.address, + gas: '1000000' + }) + + // Send a trivial transaction to generate a new block since the watcher + // is configured to wait 1 confirmation block + await generateNewBlock(foreignWeb3, user.address) + + // check that transfer and swap are not processed in the next blocks. + await promiseRetry(async (retry, number) => { + const balanceOnHome = await homeWeb3.eth.getBalance(user.address) + const currentBridgeErc20TokenBalance = await erc20Token.methods.balanceOf(COMMON_FOREIGN_BRIDGE_ADDRESS).call() + const currentBridgeHalfDuplexBalance = await halfDuplexToken.methods + .balanceOf(COMMON_FOREIGN_BRIDGE_ADDRESS) + .call() + + assert(toBN(balanceOnHome).eq(toBN(originalBalanceOnHome)), 'User balance should be the same') + assert( + toBN(currentBridgeHalfDuplexBalance).eq(toBN(bridgeHalfDuplexBalance).add(toBN(valueToTransfer))), + 'Half duplex balance should be the value of transfer' + ) + assert(toBN(currentBridgeErc20TokenBalance).eq(toBN(bridgeErc20TokenBalance)), 'erc20 balance should not change') + + // generate new blocks + await generateNewBlock(foreignWeb3, user.address) + + // after several retries, the state is corrects + if (number < 4) { + retry() + } + }) + + // let's undo the Emergency Shutdown to check that the oracle is still working + await saiTop.methods.setCaged('0').send({ + from: user.address, + gas: '1000000' + }) + + const newValueToTransfer = foreignWeb3.utils.toWei('2', 'ether') + + await halfDuplexToken.methods.transfer(COMMON_FOREIGN_BRIDGE_ADDRESS, newValueToTransfer).send({ + from: user.address, + gas: '1000000' + }) + + await generateNewBlock(foreignWeb3, user.address) + + await promiseRetry(async retry => { + const userBalance = await homeWeb3.eth.getBalance(user.address) + const updatedBridgeErc20TokenBalance = await erc20Token.methods.balanceOf(COMMON_FOREIGN_BRIDGE_ADDRESS).call() + + if ( + toBN(userBalance).lte(toBN(originalBalanceOnHome)) || + toBN(updatedBridgeErc20TokenBalance).lte(toBN(bridgeErc20TokenBalance)) + ) { + retry() + } else { + assert( + toBN(userBalance).eq(toBN(originalBalanceOnHome).add(toBN(newValueToTransfer))), + 'User balance should be increased by the half duplex token transfer' + ) + const updatedHDBalance = await halfDuplexToken.methods.balanceOf(COMMON_FOREIGN_BRIDGE_ADDRESS).call() + assert(toBN(updatedHDBalance).isZero(), 'Half duplex bridge balance should be zero') + assert( + toBN(updatedBridgeErc20TokenBalance).eq( + toBN(bridgeErc20TokenBalance) + .add(toBN(valueToTransfer)) + .add(toBN(newValueToTransfer)) + ), + 'Erc20 token balance should be correctly increased by the token swap' + ) + } + }) + }) it('should convert coins in home to tokens in foreign', async () => { const originalBalance = await erc20Token.methods.balanceOf(user.address).call() diff --git a/oracle/config/half-duplex-transfer-watcher.config.js b/oracle/config/half-duplex-transfer-watcher.config.js new file mode 100644 index 00000000..1ec94228 --- /dev/null +++ b/oracle/config/half-duplex-transfer-watcher.config.js @@ -0,0 +1,39 @@ +const baseConfig = require('./base.config') +const { ERC20_ABI } = require('../../commons') +const { EXIT_CODES } = require('../src/utils/constants') + +const initialChecksJson = process.argv[3] + +if (!initialChecksJson) { + throw new Error('initial check parameter was not provided.') +} + +let initialChecks +try { + initialChecks = JSON.parse(initialChecksJson) +} catch (e) { + throw new Error('Error on decoding values from initial checks.') +} + +const id = `${baseConfig.id}-half-duplex-transfer` + +const transferWatcherRequired = baseConfig.id === 'erc-native' + +if (!transferWatcherRequired) { + console.error(`Transfer watcher not required for bridge mode ${process.env.ORACLE_BRIDGE_MODE}`) + process.exit(EXIT_CODES.WATCHER_NOT_REQUIRED) +} + +module.exports = { + ...baseConfig.bridgeConfig, + ...baseConfig.foreignConfig, + event: 'Transfer', + eventContractAddress: initialChecks.halfDuplexTokenAddress, + eventAbi: ERC20_ABI, + eventFilter: { to: process.env.COMMON_FOREIGN_BRIDGE_ADDRESS }, + queue: 'home', + workerQueue: 'swap-tokens', + name: `watcher-${id}`, + id, + idle: initialChecks.idle +} diff --git a/oracle/config/swap-tokens-worker.config.js b/oracle/config/swap-tokens-worker.config.js new file mode 100644 index 00000000..9ef53d85 --- /dev/null +++ b/oracle/config/swap-tokens-worker.config.js @@ -0,0 +1,20 @@ +const baseConfig = require('./base.config') +const { EXIT_CODES } = require('../src/utils/constants') + +const id = `${baseConfig.id}-swap-tokens` + +const workerRequired = baseConfig.id === 'erc-native' + +if (!workerRequired) { + console.error(`Swap tokens worker not required for bridge mode ${process.env.ORACLE_BRIDGE_MODE}`) + process.exit(EXIT_CODES.WATCHER_NOT_REQUIRED) +} + +module.exports = { + ...baseConfig.bridgeConfig, + ...baseConfig.foreignConfig, + workerQueue: 'swap-tokens', + senderQueue: 'foreign', + name: `worker-${id}`, + id +} diff --git a/oracle/docker-compose-erc-native.yml b/oracle/docker-compose-erc-native.yml new file mode 100644 index 00000000..ebf5413b --- /dev/null +++ b/oracle/docker-compose-erc-native.yml @@ -0,0 +1,126 @@ +--- +version: '2.4' +services: + rabbit: + extends: + file: docker-compose.yml + service: rabbit + networks: + - net_rabbit_bridge_transfer + redis: + extends: + file: docker-compose.yml + service: redis + networks: + - net_db_bridge_transfer + bridge_request: + extends: + file: docker-compose.yml + service: bridge_request + networks: + - net_db_bridge_request + - net_rabbit_bridge_request + bridge_collected: + extends: + file: docker-compose.yml + service: bridge_collected + networks: + - net_db_bridge_request + - net_rabbit_bridge_request + bridge_affirmation: + extends: + file: docker-compose.yml + service: bridge_affirmation + networks: + - net_db_bridge_request + - net_rabbit_bridge_request + bridge_transfer: + cpus: 0.1 + mem_limit: 500m + build: + context: .. + dockerfile: oracle/Dockerfile + env_file: ./.env + environment: + - NODE_ENV=production + - ORACLE_VALIDATOR_ADDRESS=${ORACLE_VALIDATOR_ADDRESS} + restart: unless-stopped + entrypoint: yarn watcher:transfer + networks: + - net_db_bridge_transfer + - net_rabbit_bridge_transfer + bridge_half_duplex_transfer: + cpus: 0.1 + mem_limit: 500m + build: + context: .. + dockerfile: oracle/Dockerfile + env_file: ./.env + environment: + - NODE_ENV=production + - ORACLE_VALIDATOR_ADDRESS=${ORACLE_VALIDATOR_ADDRESS} + restart: unless-stopped + entrypoint: yarn watcher:half-duplex-transfer + networks: + - net_db_bridge_half_duplex_transfer + - net_rabbit_bridge_half_duplex_transfer + bridge_swap_tokens_worker: + cpus: 0.1 + mem_limit: 500m + build: + context: .. + dockerfile: oracle/Dockerfile + env_file: ./.env + environment: + - NODE_ENV=production + - ORACLE_VALIDATOR_ADDRESS=${ORACLE_VALIDATOR_ADDRESS} + restart: unless-stopped + entrypoint: yarn worker:swap-tokens + networks: + - net_rabbit_bridge_swap_tokens_worker + bridge_senderhome: + extends: + file: docker-compose.yml + service: bridge_senderhome + networks: + - net_db_bridge_request + - net_rabbit_bridge_request + bridge_senderforeign: + extends: + file: docker-compose.yml + service: bridge_senderforeign + networks: + - net_db_bridge_request + - net_rabbit_bridge_request + +networks: + net_db_bridge_request: + driver: bridge + net_db_bridge_collected: + driver: bridge + net_db_bridge_affirmation: + driver: bridge + net_db_bridge_transfer: + driver: bridge + net_db_bridge_half_duplex_transfer: + driver: bridge + net_db_bridge_senderhome: + driver: bridge + net_db_bridge_senderforeign: + driver: bridge + net_rabbit_bridge_request: + driver: bridge + net_rabbit_bridge_collected: + driver: bridge + net_rabbit_bridge_affirmation: + driver: bridge + net_rabbit_bridge_transfer: + driver: bridge + net_rabbit_bridge_half_duplex_transfer: + driver: bridge + net_rabbit_bridge_swap_tokens_worker: + driver: bridge + net_rabbit_bridge_senderhome: + driver: bridge + net_rabbit_bridge_senderforeign: + driver: bridge diff --git a/oracle/package.json b/oracle/package.json index 66b3d3f6..83f168ce 100644 --- a/oracle/package.json +++ b/oracle/package.json @@ -9,9 +9,11 @@ "watcher:collected-signatures": "./scripts/start-worker.sh watcher collected-signatures-watcher", "watcher:affirmation-request": "./scripts/start-worker.sh watcher affirmation-request-watcher", "watcher:transfer": "./scripts/start-worker.sh watcher transfer-watcher", + "watcher:half-duplex-transfer": "./scripts/start-worker.sh watcher half-duplex-transfer-watcher", + "worker:swap-tokens": "./scripts/start-worker.sh worker swap-tokens-worker", "sender:home": "./scripts/start-worker.sh sender home-sender", "sender:foreign": "./scripts/start-worker.sh sender foreign-sender", - "dev": "concurrently -n 'watcher:signature-request,watcher:collected-signatures,watcher:affirmation-request,watcher:transfer,sender:home,sender:foreign' -c 'red,green,yellow,blue,magenta,cyan' 'yarn watcher:signature-request' 'yarn watcher:collected-signatures' 'yarn watcher:affirmation-request' 'yarn watcher:transfer' 'yarn sender:home' 'yarn sender:foreign'", + "dev": "concurrently -n 'watcher:signature-request,watcher:collected-signatures,watcher:affirmation-request,watcher:transfer,watcher:half-duplex-transfer, worker:swap-tokens, sender:home,sender:foreign' -c 'red,green,yellow,blue,white,gray,magenta,cyan' 'yarn watcher:signature-request' 'yarn watcher:collected-signatures' 'yarn watcher:affirmation-request' 'yarn watcher:transfer' 'yarn watcher:half-duplex-transfer' 'yarn worker:swap-tokens' 'yarn sender:home' 'yarn sender:foreign'", "test": "NODE_ENV=test mocha", "test:watch": "NODE_ENV=test mocha --watch --reporter=min", "coverage": "NODE_ENV=test nyc --reporter=text --reporter=html mocha", diff --git a/oracle/scripts/initialChecks.js b/oracle/scripts/initialChecks.js index 0fa5557b..3e2ad574 100644 --- a/oracle/scripts/initialChecks.js +++ b/oracle/scripts/initialChecks.js @@ -1,5 +1,6 @@ require('../env') const Web3 = require('web3') +const { getTokensState } = require('../src/utils/tokenState') const { ERC677_BRIDGE_TOKEN_ABI, FOREIGN_ERC_TO_ERC_ABI, @@ -9,7 +10,7 @@ const { async function initialChecks() { const { ORACLE_BRIDGE_MODE, COMMON_FOREIGN_RPC_URL, COMMON_FOREIGN_BRIDGE_ADDRESS } = process.env - const result = {} + let result = {} const foreignWeb3 = new Web3(new Web3.providers.HttpProvider(COMMON_FOREIGN_RPC_URL)) if (ORACLE_BRIDGE_MODE === 'ERC_TO_ERC') { @@ -17,7 +18,7 @@ async function initialChecks() { result.bridgeableTokenAddress = await bridge.methods.erc20token().call() } else if (ORACLE_BRIDGE_MODE === 'ERC_TO_NATIVE') { const bridge = new foreignWeb3.eth.Contract(FOREIGN_ERC_TO_NATIVE_ABI, COMMON_FOREIGN_BRIDGE_ADDRESS) - result.bridgeableTokenAddress = await bridge.methods.erc20token().call() + result = await getTokensState(bridge) } if (ORACLE_BRIDGE_MODE === 'ERC_TO_ERC') { diff --git a/oracle/src/events/processHalfDuplexTransfers/index.js b/oracle/src/events/processHalfDuplexTransfers/index.js new file mode 100644 index 00000000..0bd9b881 --- /dev/null +++ b/oracle/src/events/processHalfDuplexTransfers/index.js @@ -0,0 +1,134 @@ +require('../../../env') +const promiseLimit = require('promise-limit') +const { HttpListProviderError } = require('http-list-provider') +const { BRIDGE_VALIDATORS_ABI, ZERO_ADDRESS } = require('../../../../commons') +const rootLogger = require('../../services/logger') +const { web3Home, web3Foreign } = require('../../services/web3') +const { AlreadyProcessedError, AlreadySignedError, InvalidValidatorError } = require('../../utils/errors') +const { EXIT_CODES, MAX_CONCURRENT_EVENTS } = require('../../utils/constants') +const estimateGas = require('../processAffirmationRequests/estimateGas') + +const limit = promiseLimit(MAX_CONCURRENT_EVENTS) + +let validatorContract = null + +function processTransfersBuilder(config) { + const homeBridge = new web3Home.eth.Contract(config.homeBridgeAbi, config.homeBridgeAddress) + const foreignBridge = new web3Foreign.eth.Contract(config.foreignBridgeAbi, config.foreignBridgeAddress) + const userRequestForAffirmationAbi = config.foreignBridgeAbi.filter( + e => e.type === 'event' && e.name === 'UserRequestForAffirmation' + )[0] + const tokensSwappedAbi = config.foreignBridgeAbi.filter(e => e.type === 'event' && e.name === 'TokensSwapped')[0] + const userRequestForAffirmationHash = web3Home.eth.abi.encodeEventSignature(userRequestForAffirmationAbi) + const tokensSwappedHash = tokensSwappedAbi ? web3Home.eth.abi.encodeEventSignature(tokensSwappedAbi) : '0x' + + return async function processTransfers(transfers, blockNumber) { + const txToSend = [] + + if (validatorContract === null) { + rootLogger.debug('Getting validator contract address') + const validatorContractAddress = await homeBridge.methods.validatorContract().call() + rootLogger.debug({ validatorContractAddress }, 'Validator contract address obtained') + + validatorContract = new web3Home.eth.Contract(BRIDGE_VALIDATORS_ABI, validatorContractAddress) + } + + rootLogger.debug(`Processing ${transfers.length} Transfer events`) + const callbacks = transfers + .map(transfer => async () => { + const { from, value } = transfer.returnValues + + const logger = rootLogger.child({ + eventTransactionHash: transfer.transactionHash + }) + + logger.info({ from, value }, `Processing transfer ${transfer.transactionHash}`) + + const block = await web3Foreign.eth.getBlock(blockNumber) + logger.debug({ blockNumber, timestamp: block.timestamp }, `Block obtained`) + + const tokenSwapAllowed = await foreignBridge.methods.isTokenSwapAllowed(block.timestamp).call() + + if (!tokenSwapAllowed) { + logger.info( + `Transfer event discarded because SCD Emergency Shutdown has happened ${transfer.transactionHash}` + ) + return + } + + const receipt = await web3Foreign.eth.getTransactionReceipt(transfer.transactionHash) + + const existsAffirmationEvent = receipt.logs.some( + e => e.address === config.foreignBridgeAddress && e.topics[0] === userRequestForAffirmationHash + ) + + if (existsAffirmationEvent) { + logger.info( + `Transfer event discarded because a transaction with alternative receiver detected in transaction ${ + transfer.transactionHash + }` + ) + return + } + + const existsTokensSwappedEvent = tokensSwappedAbi + ? receipt.logs.some(e => e.address === config.foreignBridgeAddress && e.topics[0] === tokensSwappedHash) + : false + + if (from === ZERO_ADDRESS && existsTokensSwappedEvent) { + logger.info( + `Transfer event discarded because token swap is detected in transaction ${transfer.transactionHash}` + ) + return + } + + let gasEstimate + try { + logger.debug('Estimate gas') + gasEstimate = await estimateGas({ + web3: web3Home, + homeBridge, + validatorContract, + recipient: from, + value, + txHash: transfer.transactionHash, + address: config.validatorAddress + }) + logger.debug({ gasEstimate }, 'Gas estimated') + } catch (e) { + if (e instanceof HttpListProviderError) { + throw new Error('RPC Connection Error: submitSignature Gas Estimate cannot be obtained.') + } else if (e instanceof InvalidValidatorError) { + logger.fatal({ address: config.validatorAddress }, 'Invalid validator') + process.exit(EXIT_CODES.INCOMPATIBILITY) + } else if (e instanceof AlreadySignedError) { + logger.info(`Already signed transfer ${transfer.transactionHash}`) + return + } else if (e instanceof AlreadyProcessedError) { + logger.info(`transfer ${transfer.transactionHash} was already processed by other validators`) + return + } else { + logger.error(e, 'Unknown error while processing transaction') + throw e + } + } + + const data = await homeBridge.methods + .executeAffirmation(from, value, transfer.transactionHash) + .encodeABI({ from: config.validatorAddress }) + + txToSend.push({ + data, + gasEstimate, + transactionReference: transfer.transactionHash, + to: config.homeBridgeAddress + }) + }) + .map(promise => limit(promise)) + + await Promise.all(callbacks) + return txToSend + } +} + +module.exports = processTransfersBuilder diff --git a/oracle/src/services/amqpClient.js b/oracle/src/services/amqpClient.js index fddbd35d..9dc2b2c8 100644 --- a/oracle/src/services/amqpClient.js +++ b/oracle/src/services/amqpClient.js @@ -11,17 +11,23 @@ connection.on('disconnect', () => { logger.error('Disconnected from amqp Broker') }) -function connectWatcherToQueue({ queueName, cb }) { +function connectWatcherToQueue({ queueName, workerQueue, cb }) { + const queueList = workerQueue ? [queueName, workerQueue] : [queueName] + const channelWrapper = connection.createChannel({ json: true, setup(channel) { - return Promise.all([channel.assertQueue(queueName, { durable: true })]) + return Promise.all(queueList.map(queue => channel.assertQueue(queue, { durable: true }))) } }) const sendToQueue = data => channelWrapper.sendToQueue(queueName, data, { persistent: true }) + let sendToWorker + if (workerQueue) { + sendToWorker = data => channelWrapper.sendToQueue(workerQueue, data, { persistent: true }) + } - cb({ sendToQueue, channel: channelWrapper }) + cb({ sendToQueue, sendToWorker, channel: channelWrapper }) } function connectSenderToQueue({ queueName, cb }) { @@ -59,6 +65,43 @@ function connectSenderToQueue({ queueName, cb }) { }) } +function connectWorkerToQueue({ queueName, senderQueue, cb }) { + const deadLetterExchange = `${queueName}-retry` + + const channelWrapper = connection.createChannel({ + json: true + }) + + channelWrapper.addSetup(channel => { + return Promise.all([ + channel.assertExchange(deadLetterExchange, 'fanout', { durable: true }), + channel.assertQueue(queueName, { durable: true }), + channel.assertQueue(senderQueue, { durable: true }), + channel.bindQueue(queueName, deadLetterExchange), + channel.prefetch(1), + channel.consume(queueName, msg => + cb({ + msg, + channel: channelWrapper, + ackMsg: job => channelWrapper.ack(job), + nackMsg: job => channelWrapper.nack(job, false, true), + sendToSenderQueue: data => channelWrapper.sendToQueue(senderQueue, data, { persistent: true }), + scheduleForRetry: async (data, msgRetries = 0) => { + await generateRetry({ + data, + msgRetries, + channelWrapper, + channel, + queueName, + deadLetterExchange + }) + } + }) + ) + ]) + }) +} + async function generateRetry({ data, msgRetries, channelWrapper, channel, queueName, deadLetterExchange }) { const retries = msgRetries + 1 const delay = getRetrySequence(retries) * 1000 @@ -78,6 +121,7 @@ async function generateRetry({ data, msgRetries, channelWrapper, channel, queueN module.exports = { connectWatcherToQueue, connectSenderToQueue, + connectWorkerToQueue, connection, generateRetry } diff --git a/oracle/src/utils/tokenState.js b/oracle/src/utils/tokenState.js new file mode 100644 index 00000000..36e4c648 --- /dev/null +++ b/oracle/src/utils/tokenState.js @@ -0,0 +1,20 @@ +async function getTokensState(bridgeContract) { + const context = {} + context.bridgeableTokenAddress = await bridgeContract.methods.erc20token().call() + try { + const halfDuplexErc20tokenAddress = await bridgeContract.methods.halfDuplexErc20token().call() + if (halfDuplexErc20tokenAddress !== context.bridgeableTokenAddress) { + context.halfDuplexTokenAddress = halfDuplexErc20tokenAddress + } else { + context.idle = true + } + } catch (e) { + context.idle = true + } + + return context +} + +module.exports = { + getTokensState +} diff --git a/oracle/src/watcher.js b/oracle/src/watcher.js index 388bfb51..fd202d8d 100644 --- a/oracle/src/watcher.js +++ b/oracle/src/watcher.js @@ -21,16 +21,21 @@ const processSignatureRequests = require('./events/processSignatureRequests')(co const processCollectedSignatures = require('./events/processCollectedSignatures')(config) const processAffirmationRequests = require('./events/processAffirmationRequests')(config) const processTransfers = require('./events/processTransfers')(config) +const processHalfDuplexTransfers = require('./events/processHalfDuplexTransfers')(config) const processAMBSignatureRequests = require('./events/processAMBSignatureRequests')(config) const processAMBCollectedSignatures = require('./events/processAMBCollectedSignatures')(config) const processAMBAffirmationRequests = require('./events/processAMBAffirmationRequests')(config) +const { getTokensState } = require('./utils/tokenState') + const ZERO = toBN(0) const ONE = toBN(1) const web3Instance = config.web3 const bridgeContract = new web3Instance.eth.Contract(config.bridgeAbi, config.bridgeContractAddress) -const eventContract = new web3Instance.eth.Contract(config.eventAbi, config.eventContractAddress) +let { eventContractAddress } = config +let eventContract = new web3Instance.eth.Contract(config.eventAbi, eventContractAddress) +let skipEvents = config.idle const lastBlockRedisKey = `${config.id}:lastProcessedBlock` let lastProcessedBlock = BN.max(config.startBlock.sub(ONE), ZERO) @@ -44,6 +49,7 @@ async function initialize() { await getLastProcessedBlock() connectWatcherToQueue({ queueName: config.queue, + workerQueue: config.workerQueue, cb: runMain }) } catch (e) { @@ -52,16 +58,16 @@ async function initialize() { } } -async function runMain({ sendToQueue }) { +async function runMain({ sendToQueue, sendToWorker }) { try { if (connection.isConnected() && redis.status === 'ready') { if (config.maxProcessingTime) { - await watchdog(() => main({ sendToQueue }), config.maxProcessingTime, () => { + await watchdog(() => main({ sendToQueue, sendToWorker }), config.maxProcessingTime, () => { logger.fatal('Max processing time reached') process.exit(EXIT_CODES.MAX_TIME_REACHED) }) } else { - await main({ sendToQueue }) + await main({ sendToQueue, sendToWorker }) } } } catch (e) { @@ -69,7 +75,7 @@ async function runMain({ sendToQueue }) { } setTimeout(() => { - runMain({ sendToQueue }) + runMain({ sendToQueue, sendToWorker }) }, config.pollingInterval) } @@ -84,7 +90,7 @@ function updateLastProcessedBlock(lastBlockNumber) { return redis.set(lastBlockRedisKey, lastProcessedBlock.toString()) } -function processEvents(events) { +function processEvents(events, blockNumber) { switch (config.id) { case 'native-erc-signature-request': case 'erc-erc-signature-request': @@ -102,6 +108,8 @@ function processEvents(events) { case 'erc-erc-transfer': case 'erc-native-transfer': return processTransfers(events) + case 'erc-native-half-duplex-transfer': + return processHalfDuplexTransfers(events, blockNumber) case 'amb-signature-request': return processAMBSignatureRequests(events) case 'amb-collected-signatures': @@ -113,6 +121,29 @@ function processEvents(events) { } } +async function checkConditions() { + let state + switch (config.id) { + case 'erc-native-transfer': + state = await getTokensState(bridgeContract) + updateEventContract(state.bridgeableTokenAddress) + break + case 'erc-native-half-duplex-transfer': + state = await getTokensState(bridgeContract) + skipEvents = state.idle + updateEventContract(state.halfDuplexTokenAddress) + break + default: + } +} + +function updateEventContract(address) { + if (eventContractAddress !== address) { + eventContractAddress = address + eventContract = new web3Instance.eth.Contract(config.eventAbi, eventContractAddress) + } +} + async function getLastBlockToProcess() { const lastBlockNumberPromise = getBlockNumber(web3Instance).then(toBN) const requiredBlockConfirmationsPromise = getRequiredBlockConfirmations(bridgeContract).then(toBN) @@ -124,8 +155,15 @@ async function getLastBlockToProcess() { return lastBlockNumber.sub(requiredBlockConfirmations) } -async function main({ sendToQueue }) { +async function main({ sendToQueue, sendToWorker }) { try { + await checkConditions() + + if (skipEvents) { + logger.debug('Watcher in idle mode, skipping getting events') + return + } + const lastBlockToProcess = await getLastBlockToProcess() if (lastBlockToProcess.lte(lastProcessedBlock)) { @@ -146,7 +184,11 @@ async function main({ sendToQueue }) { logger.info(`Found ${events.length} ${config.event} events`) if (events.length) { - const job = await processEvents(events) + if (sendToWorker) { + await sendToWorker({ blockNumber: toBlock.toString() }) + } + + const job = await processEvents(events, toBlock.toString()) logger.info('Transactions to send:', job.length) if (job.length) { diff --git a/oracle/src/worker.js b/oracle/src/worker.js new file mode 100644 index 00000000..17d7631d --- /dev/null +++ b/oracle/src/worker.js @@ -0,0 +1,73 @@ +const path = require('path') +const logger = require('./services/logger') +const rpcUrlsManager = require('./services/getRpcUrlsManager') +const { checkHTTPS, watchdog } = require('./utils/utils') +const { EXIT_CODES } = require('./utils/constants') +const { connectWorkerToQueue } = require('./services/amqpClient') + +const config = require(path.join('../config/', process.argv[2])) + +const swapTokens = require('./workers/swapTokens')(config) + +async function initialize() { + try { + const checkHttps = checkHTTPS(process.env.ORACLE_ALLOW_HTTP_FOR_RPC, logger) + + rpcUrlsManager.homeUrls.forEach(checkHttps('home')) + rpcUrlsManager.foreignUrls.forEach(checkHttps('foreign')) + + connectWorkerToQueue({ + queueName: config.workerQueue, + senderQueue: config.senderQueue, + cb: options => { + if (config.maxProcessingTime) { + return watchdog(() => main(options), config.maxProcessingTime, () => { + logger.fatal('Max processing time reached') + process.exit(EXIT_CODES.MAX_TIME_REACHED) + }) + } + + return main(options) + } + }) + } catch (e) { + logger.error(e.message) + process.exit(EXIT_CODES.GENERAL_ERROR) + } +} + +async function run(blockNumber) { + if (config.id === 'erc-native-swap-tokens') { + return swapTokens(blockNumber) + } else { + return [] + } +} + +async function main({ msg, ackMsg, nackMsg, sendToSenderQueue, scheduleForRetry }) { + try { + const { blockNumber } = JSON.parse(msg.content) + logger.info(`Msg received with block number ${blockNumber}`) + + try { + const job = await run(blockNumber) + + logger.info('Transactions to send:', job.length) + + if (job.length) { + await sendToSenderQueue(job) + } + } catch (e) { + logger.info(`Sending failed msg to retry`) + await scheduleForRetry({ blockNumber }, msg.properties.headers['x-retries']) + } + + ackMsg(msg) + } catch (e) { + logger.error(e) + nackMsg(msg) + } + logger.debug(`Finished worker operation`) +} + +initialize() diff --git a/oracle/src/workers/swapTokens.js b/oracle/src/workers/swapTokens.js new file mode 100644 index 00000000..9635df8c --- /dev/null +++ b/oracle/src/workers/swapTokens.js @@ -0,0 +1,111 @@ +require('../../env') +const { HttpListProviderError } = require('http-list-provider') +const rootLogger = require('../services/logger') +const { web3Foreign } = require('../services/web3') + +const { BRIDGE_VALIDATORS_ABI, ERC20_ABI } = require('../../../commons') + +let validatorContract = null +let halfDuplexTokenContract = null + +function swapTokensBuilder(config) { + const foreignBridge = new web3Foreign.eth.Contract(config.foreignBridgeAbi, config.foreignBridgeAddress) + + return async function swapTokens(blockNumber) { + const txToSend = [] + + const logger = rootLogger.child({ + blockNumber: blockNumber.toString() + }) + + logger.debug(`Starting swap tokens operation`) + + if (validatorContract === null) { + logger.debug('Getting validator contract address') + const validatorContractAddress = await foreignBridge.methods.validatorContract().call() + logger.debug({ validatorContractAddress }, 'Validator contract address obtained') + + validatorContract = new web3Foreign.eth.Contract(BRIDGE_VALIDATORS_ABI, validatorContractAddress) + } + + logger.debug(`Checking if is validator duty`) + const validatorDuty = await validatorContract.methods.isValidatorDuty(config.validatorAddress).call() + + if (!validatorDuty) { + logger.info(`Token swap discarded because is not validator duty`) + return txToSend + } + + logger.debug(`Checking if half duplex token balance is above the threshold`) + const hdTokenBalanceAboveMinBalance = await foreignBridge.methods.isHDTokenBalanceAboveMinBalance().call() + + if (!hdTokenBalanceAboveMinBalance) { + logger.info(`Token swap discarded because half duplex balance is below the threshold`) + return txToSend + } + + const block = await web3Foreign.eth.getBlock(blockNumber) + logger.debug({ timestamp: block.timestamp }, `Block obtained`) + + logger.debug(`Checking if SCD Emergency Shutdown has happened`) + const tokenSwapAllowed = await foreignBridge.methods.isTokenSwapAllowed(block.timestamp).call() + + if (!tokenSwapAllowed) { + logger.info(`Token swap discarded because SCD Emergency Shutdown has happened`) + return txToSend + } + + let gasEstimate + + try { + logger.debug(`Estimate gas`) + gasEstimate = await foreignBridge.methods.swapTokens().estimateGas({ + from: config.validatorAddress + }) + + logger.debug({ gasEstimate }, 'Gas estimated') + } catch (e) { + if (e instanceof HttpListProviderError) { + const errorMsg = 'RPC Connection Error: swapTokens Gas Estimate cannot be obtained.' + logger.error(e, errorMsg) + throw new Error(errorMsg) + } else { + if (halfDuplexTokenContract === null) { + logger.debug('Getting half duplex token contract address') + const halfDuplexErc20Token = await foreignBridge.methods.halfDuplexErc20token().call() + logger.debug({ halfDuplexErc20Token }, 'Half duplex token contract address obtained') + + halfDuplexTokenContract = new web3Foreign.eth.Contract(ERC20_ABI, halfDuplexErc20Token) + } + + const balance = web3Foreign.utils.toBN( + await halfDuplexTokenContract.methods.balanceOf(config.foreignBridgeAddress).call() + ) + logger.debug({ balance: balance.toString() }, 'Half duplex token bridge balance obtained') + + if (balance.isZero()) { + logger.info(`Gas estimate failed because half duplex token balance is zero. Tokens swap is discarded.`) + return txToSend + } + + logger.error(e, 'Unknown error while processing transaction') + throw e + } + } + + // generate data + const data = await foreignBridge.methods.swapTokens().encodeABI() + + // push to job + txToSend.push({ + data, + gasEstimate, + transactionReference: `swap tokens operation for block number ${blockNumber.toString()}`, + to: config.foreignBridgeAddress + }) + + return txToSend + } +} + +module.exports = swapTokensBuilder diff --git a/parity/chain-foreign.json b/parity/chain-foreign.json index c10df2bd..67f2cc60 100644 --- a/parity/chain-foreign.json +++ b/parity/chain-foreign.json @@ -1,53 +1,135 @@ { - "name": "DevelopmentChain", - "engine": { - "instantSeal": null - }, - "params": { - "gasLimitBoundDivisor": "0x0400", - "accountStartNonce": "0x0", - "maximumExtraDataSize": "0x20", - "minGasLimit": "0x1388", - "networkID" : "0x2A", - "registrar" : "0x0000000000000000000000000000000000001337", - "eip150Transition": "0x0", - "eip160Transition": "0x0", - "eip161abcTransition": "0x0", - "eip161dTransition": "0x0", - "eip155Transition": "0x0", - "eip98Transition": "0x7fffffffffffff", - "maxCodeSize": 24576, - "maxCodeSizeTransition": "0x0", - "eip140Transition": "0x0", - "eip211Transition": "0x0", - "eip214Transition": "0x0", - "eip658Transition": "0x0", - "wasmActivationTransition": "0x0" - }, - "genesis": { - "seal": { - "generic": "0x0" - }, - "difficulty": "0x20000", - "author": "0x0000000000000000000000000000000000000000", - "timestamp": "0x00", - "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "extraData": "0x", - "gasLimit": "0x7A1200" - }, - "accounts": { - "0000000000000000000000000000000000000001": { "balance": "1", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } }, - "0000000000000000000000000000000000000002": { "balance": "1", "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } }, - "0000000000000000000000000000000000000003": { "balance": "1", "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } }, - "0000000000000000000000000000000000000004": { "balance": "1", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } }, - "0000000000000000000000000000000000000005": { "balance": "1", "builtin": { "name": "modexp", "activate_at": 0, "pricing": { "modexp": { "divisor": 20 } } } }, - "0000000000000000000000000000000000000006": { "balance": "1", "builtin": { "name": "alt_bn128_add", "activate_at": 0, "pricing": { "linear": { "base": 500, "word": 0 } } } }, - "0000000000000000000000000000000000000007": { "balance": "1", "builtin": { "name": "alt_bn128_mul", "activate_at": 0, "pricing": { "linear": { "base": 40000, "word": 0 } } } }, - "0000000000000000000000000000000000000008": { "balance": "1", "builtin": { "name": "alt_bn128_pairing", "activate_at": 0, "pricing": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 } } } }, - "0000000000000000000000000000000000001337": { "balance": "1", "constructor": "0x606060405233600060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550670de0b6b3a764000060035534610000575b612904806100666000396000f3006060604052361561013c576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806306b2ff471461014157806313af40351461018c57806319362a28146101bf5780633f3935d114610248578063432ced04146102b75780634f39ca59146102eb5780636795dbcd1461032457806369fe0e2d146103c857806379ce9fac146103fd5780638da5cb5b1461045557806390b97fc1146104a457806392698814146105245780639890220b1461055d578063ac4e73f914610584578063ac72c12014610612578063c3a358251461064b578063ddca3f43146106c3578063deb931a2146106e6578063df57b74214610747578063e30bd740146107a8578063eadf976014610862578063ef5454d6146108e7578063f25eb5c114610975578063f6d339e414610984575b610000565b3461000057610172600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610a1f565b604051808215151515815260200191505060405180910390f35b34610000576101bd600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610a81565b005b346100005761022e60048080356000191690602001909190803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509190803560001916906020019091905050610ba2565b604051808215151515815260200191505060405180910390f35b346100005761029d600480803590602001908201803590602001908080601f01602080910402602001604051908101604052809392919081815260200183838082843782019150505050505091905050610dc9565b604051808215151515815260200191505060405180910390f35b6102d1600480803560001916906020019091905050611035565b604051808215151515815260200191505060405180910390f35b346100005761030a60048080356000191690602001909190505061115f565b604051808215151515815260200191505060405180910390f35b346100005761038660048080356000191690602001909190803590602001908201803590602001908080601f01602080910402602001604051908101604052809392919081815260200183838082843782019150505050505091905050611378565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34610000576103e3600480803590602001909190505061140d565b604051808215151515815260200191505060405180910390f35b346100005761043b60048080356000191690602001909190803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506114b4565b604051808215151515815260200191505060405180910390f35b34610000576104626115fb565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b346100005761050660048080356000191690602001909190803590602001908201803590602001908080601f01602080910402602001604051908101604052809392919081815260200183838082843782019150505050505091905050611621565b60405180826000191660001916815260200191505060405180910390f35b34610000576105436004808035600019169060200190919050506116b2565b604051808215151515815260200191505060405180910390f35b346100005761056a611715565b604051808215151515815260200191505060405180910390f35b34610000576105f8600480803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509190803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050611824565b604051808215151515815260200191505060405180910390f35b3461000057610631600480803560001916906020019091905050611d8b565b604051808215151515815260200191505060405180910390f35b34610000576106ad60048080356000191690602001909190803590602001908201803590602001908080601f01602080910402602001604051908101604052809392919081815260200183838082843782019150505050505091905050611dee565b6040518082815260200191505060405180910390f35b34610000576106d0611e83565b6040518082815260200191505060405180910390f35b3461000057610705600480803560001916906020019091905050611e89565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b3461000057610766600480803560001916906020019091905050611ed2565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34610000576107d9600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050611f1b565b6040518080602001828103825283818151815260200191508051906020019080838360008314610828575b80518252602083111561082857602082019150602081019050602083039250610804565b505050905090810190601f1680156108545780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34610000576108cd60048080356000191690602001909190803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509190803590602001909190505061200c565b604051808215151515815260200191505060405180910390f35b346100005761095b600480803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509190803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050612236565b604051808215151515815260200191505060405180910390f35b3461000057610982612425565b005b3461000057610a0560048080356000191690602001909190803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509190803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050612698565b604051808215151515815260200191505060405180910390f35b60006000600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020805460018160011615610100020316600290049050141590505b919050565b600060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141515610add57610b9f565b8073ffffffffffffffffffffffffffffffffffffffff16600060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f70aea8d848e8a90fb7661b227dc522eb6395c3dac71b63cb59edd5c9899b236460405180905060405180910390a380600060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505b5b50565b6000833373ffffffffffffffffffffffffffffffffffffffff1660016000836000191660001916815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16141515610c1d57610dc1565b82600160008760001916600019168152602001908152602001600020600201856040518082805190602001908083835b60208310610c705780518252602082019150602081019050602083039250610c4d565b6001836020036101000a03801982511681845116808217855250505050505090500191505090815260200160405180910390208160001916905550836040518082805190602001908083835b60208310610cdf5780518252602082019150602081019050602083039250610cbc565b6001836020036101000a038019825116818451168082178552505050505050905001915050604051809103902085600019167fb829c3e412537bbe794c048ccb9e4605bb4aaaa8e4d4c15c1a6e0c2adc1716ea866040518080602001828103825283818151815260200191508051906020019080838360008314610d82575b805182526020831115610d8257602082019150602081019050602083039250610d5e565b505050905090810190601f168015610dae5780820380516001836020036101000a031916815260200191505b509250505060405180910390a3600191505b5b509392505050565b6000813373ffffffffffffffffffffffffffffffffffffffff1660016000836040518082805190602001908083835b60208310610e1b5780518252602082019150602081019050602083039250610df8565b6001836020036101000a03801982511681845116808217855250505050505090500191505060405180910390206000191660001916815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16141515610ea45761102f565b82600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000209080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10610f2d57805160ff1916838001178555610f5b565b82800160010185558215610f5b579182015b82811115610f5a578251825591602001919060010190610f3f565b5b509050610f8091905b80821115610f7c576000816000905550600101610f64565b5090565b50503373ffffffffffffffffffffffffffffffffffffffff16836040518082805190602001908083835b60208310610fcd5780518252602082019150602081019050602083039250610faa565b6001836020036101000a03801982511681845116808217855250505050505090500191505060405180910390207f098ae8581bb8bd9af1beaf7f2e9f51f31a8e5a8bfada4e303a645d71d9c9192060405180905060405180910390a3600191505b5b50919050565b600081600060016000836000191660001916815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614151561109b57611159565b6003543410156110aa57611158565b3360016000856000191660001916815260200190815260200160002060000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055503373ffffffffffffffffffffffffffffffffffffffff1683600019167f4963513eca575aba66fdcd25f267aae85958fe6fb97e75fa25d783f1a091a22160405180905060405180910390a3600191505b5b5b50919050565b6000813373ffffffffffffffffffffffffffffffffffffffff1660016000836000191660001916815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415156111da57611372565b6002600060016000866000191660001916815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020805460018160011615610100020316600290046000825580601f1061127c57506112b3565b601f0160209004906000526020600020908101906112b291905b808211156112ae576000816000905550600101611296565b5090565b5b5060016000846000191660001916815260200190815260200160002060006000820160006101000a81549073ffffffffffffffffffffffffffffffffffffffff02191690556001820160006101000a81549073ffffffffffffffffffffffffffffffffffffffff021916905550503373ffffffffffffffffffffffffffffffffffffffff1683600019167fef1961b4d2909dc23643b309bfe5c3e5646842d98c3a58517037ef3871185af360405180905060405180910390a3600191505b5b50919050565b6000600160008460001916600019168152602001908152602001600020600201826040518082805190602001908083835b602083106113cc57805182526020820191506020810190506020830392506113a9565b6001836020036101000a0380198251168184511680821785525050505050509050019150509081526020016040518091039020546001900490505b92915050565b6000600060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614151561146b576114af565b816003819055507f6bbc57480a46553fa4d156ce702beef5f3ad66303b0ed1a5d4cb44966c6584c3826040518082815260200191505060405180910390a1600190505b5b919050565b6000823373ffffffffffffffffffffffffffffffffffffffff1660016000836000191660001916815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614151561152f576115f4565b8260016000866000191660001916815260200190815260200160002060000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1685600019167f7b97c62130aa09acbbcbf7482630e756592496f1759eaf702f469cf64dfb779460405180905060405180910390a4600191505b5b5092915050565b600060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000600160008460001916600019168152602001908152602001600020600201826040518082805190602001908083835b602083106116755780518252602082019150602081019050602083039250611652565b6001836020036101000a03801982511681845116808217855250505050505090500191505090815260200160405180910390205490505b92915050565b6000600060016000846000191660001916815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16141590505b919050565b6000600060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614151561177357611821565b7fdef931299fe61d176f949118058530c1f3f539dcb6950b4e372c9b835c33ca073073ffffffffffffffffffffffffffffffffffffffff16316040518082815260200191505060405180910390a13373ffffffffffffffffffffffffffffffffffffffff166108fc3073ffffffffffffffffffffffffffffffffffffffff16319081150290604051809050600060405180830381858888f19350505050151561181b57610000565b600190505b5b90565b60006000836040518082805190602001908083835b6020831061185c5780518252602082019150602081019050602083039250611839565b6001836020036101000a03801982511681845116808217855250505050505090500191505060405180910390203373ffffffffffffffffffffffffffffffffffffffff1660016000836000191660001916815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614151561190157611d83565b846040518082805190602001908083835b602083106119355780518252602082019150602081019050602083039250611912565b6001836020036101000a03801982511681845116808217855250505050505090500191505060405180910390209150600060016000846000191660001916815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614158015611ab4575081600019166002600060016000866000191660001916815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206040518082805460018160011615610100020316600290048015611aa15780601f10611a7f576101008083540402835291820191611aa1565b820191906000526020600020905b815481529060010190602001808311611a8d575b5050915050604051809103902060001916145b15611c79576002600060016000856000191660001916815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020805460018160011615610100020316600290046000825580601f10611b5b5750611b92565b601f016020900490600052602060002090810190611b9191905b80821115611b8d576000816000905550600101611b75565b5090565b5b5060016000836000191660001916815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16856040518082805190602001908083835b60208310611c1c5780518252602082019150602081019050602083039250611bf9565b6001836020036101000a03801982511681845116808217855250505050505090500191505060405180910390207f12491ad95fd945e444d88a894ffad3c21959880a4dcd8af99d4ae4ffc71d4abd60405180905060405180910390a35b8360016000846000191660001916815260200190815260200160002060010160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508373ffffffffffffffffffffffffffffffffffffffff16856040518082805190602001908083835b60208310611d215780518252602082019150602081019050602083039250611cfe565b6001836020036101000a03801982511681845116808217855250505050505090500191505060405180910390207f728435a0031f6a04538fcdd24922a7e06bc7bc945db03e83d22122d1bc5f28df60405180905060405180910390a3600192505b5b505092915050565b6000600060016000846000191660001916815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16141590505b919050565b6000600160008460001916600019168152602001908152602001600020600201826040518082805190602001908083835b60208310611e425780518252602082019150602081019050602083039250611e1f565b6001836020036101000a0380198251168184511680821785525050505050509050019150509081526020016040518091039020546001900490505b92915050565b60035481565b600060016000836000191660001916815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690505b919050565b600060016000836000191660001916815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690505b919050565b6020604051908101604052806000815250600260008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015611fff5780601f10611fd457610100808354040283529160200191611fff565b820191906000526020600020905b815481529060010190602001808311611fe257829003601f168201915b505050505090505b919050565b6000833373ffffffffffffffffffffffffffffffffffffffff1660016000836000191660001916815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415156120875761222e565b82600102600160008760001916600019168152602001908152602001600020600201856040518082805190602001908083835b602083106120dd57805182526020820191506020810190506020830392506120ba565b6001836020036101000a03801982511681845116808217855250505050505090500191505090815260200160405180910390208160001916905550836040518082805190602001908083835b6020831061214c5780518252602082019150602081019050602083039250612129565b6001836020036101000a038019825116818451168082178552505050505050905001915050604051809103902085600019167fb829c3e412537bbe794c048ccb9e4605bb4aaaa8e4d4c15c1a6e0c2adc1716ea8660405180806020018281038252838181518152602001915080519060200190808383600083146121ef575b8051825260208311156121ef576020820191506020810190506020830392506121cb565b505050905090810190601f16801561221b5780820380516001836020036101000a031916815260200191505b509250505060405180910390a3600191505b5b509392505050565b6000600060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161415156122945761241f565b82600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000209080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061231d57805160ff191683800117855561234b565b8280016001018555821561234b579182015b8281111561234a57825182559160200191906001019061232f565b5b50905061237091905b8082111561236c576000816000905550600101612354565b5090565b50508173ffffffffffffffffffffffffffffffffffffffff16836040518082805190602001908083835b602083106123bd578051825260208201915060208101905060208303925061239a565b6001836020036101000a03801982511681845116808217855250505050505090500191505060405180910390207f098ae8581bb8bd9af1beaf7f2e9f51f31a8e5a8bfada4e303a645d71d9c9192060405180905060405180910390a3600190505b5b92915050565b3373ffffffffffffffffffffffffffffffffffffffff16600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060405180828054600181600116156101000203166002900480156124d65780601f106124b45761010080835404028352918201916124d6565b820191906000526020600020905b8154815290600101906020018083116124c2575b505091505060405180910390207f12491ad95fd945e444d88a894ffad3c21959880a4dcd8af99d4ae4ffc71d4abd60405180905060405180910390a360016000600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060405180828054600181600116156101000203166002900480156125b05780601f1061258e5761010080835404028352918201916125b0565b820191906000526020600020905b81548152906001019060200180831161259c575b505091505060405180910390206000191660001916815260200190815260200160002060010160006101000a81549073ffffffffffffffffffffffffffffffffffffffff0219169055600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020805460018160011615610100020316600290046000825580601f1061265d5750612694565b601f01602090049060005260206000209081019061269391905b8082111561268f576000816000905550600101612677565b5090565b5b505b565b6000833373ffffffffffffffffffffffffffffffffffffffff1660016000836000191660001916815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16141515612713576128d0565b8273ffffffffffffffffffffffffffffffffffffffff16600102600160008760001916600019168152602001908152602001600020600201856040518082805190602001908083835b6020831061277f578051825260208201915060208101905060208303925061275c565b6001836020036101000a03801982511681845116808217855250505050505090500191505090815260200160405180910390208160001916905550836040518082805190602001908083835b602083106127ee57805182526020820191506020810190506020830392506127cb565b6001836020036101000a038019825116818451168082178552505050505050905001915050604051809103902085600019167fb829c3e412537bbe794c048ccb9e4605bb4aaaa8e4d4c15c1a6e0c2adc1716ea866040518080602001828103825283818151815260200191508051906020019080838360008314612891575b8051825260208311156128915760208201915060208101905060208303925061286d565b505050905090810190601f1680156128bd5780820380516001836020036101000a031916815260200191505b509250505060405180910390a3600191505b5b5093925050505600a165627a7a7230582066b2da4773a0f1d81efe071c66b51c46868a871661efd18c0f629353ff4c1f9b0029" }, - "aaB52d66283F7A1D5978bcFcB55721ACB467384b": { "balance": "1606938044258990275541962092341162602522202993782792835301376" }, - "bb140FbA6242a1c3887A7823F7750a73101383e3": { "balance": "1606938044258990275541962092341162602522202993782792835301376" }, - "7FC1442AB55Da569940Eb750AaD2BAA63DA4010E": { "balance": "500000000000000000000" }, - "B4579fd5AfEaB60B03Db3F408AAdD315035943f7": { "balance": "500000000000000000000" } - } + "name": "DevelopmentChain", + "engine": { + "instantSeal": null + }, + "params": { + "gasLimitBoundDivisor": "0x0400", + "accountStartNonce": "0x0", + "maximumExtraDataSize": "0x20", + "minGasLimit": "0x1388", + "networkID": "0x2A", + "registrar": "0x0000000000000000000000000000000000001337", + "eip150Transition": "0x0", + "eip160Transition": "0x0", + "eip161abcTransition": "0x0", + "eip161dTransition": "0x0", + "eip155Transition": "0x0", + "eip98Transition": "0x7fffffffffffff", + "maxCodeSize": 24576, + "maxCodeSizeTransition": "0x0", + "eip140Transition": "0x0", + "eip211Transition": "0x0", + "eip214Transition": "0x0", + "eip658Transition": "0x0", + "wasmActivationTransition": "0x0" + }, + "genesis": { + "seal": { + "generic": "0x0" + }, + "difficulty": "0x20000", + "author": "0x0000000000000000000000000000000000000000", + "timestamp": "0x00", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "extraData": "0x", + "gasLimit": "0x7A1200" + }, + "accounts": { + "0000000000000000000000000000000000000001": { + "balance": "1", + "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } + }, + "0000000000000000000000000000000000000002": { + "balance": "1", + "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } + }, + "0000000000000000000000000000000000000003": { + "balance": "1", + "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } + }, + "0000000000000000000000000000000000000004": { + "balance": "1", + "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } + }, + "0000000000000000000000000000000000000005": { + "balance": "1", + "builtin": { "name": "modexp", "activate_at": 0, "pricing": { "modexp": { "divisor": 20 } } } + }, + "0000000000000000000000000000000000000006": { + "balance": "1", + "builtin": { "name": "alt_bn128_add", "activate_at": 0, "pricing": { "linear": { "base": 500, "word": 0 } } } + }, + "0000000000000000000000000000000000000007": { + "balance": "1", + "builtin": { "name": "alt_bn128_mul", "activate_at": 0, "pricing": { "linear": { "base": 40000, "word": 0 } } } + }, + "0000000000000000000000000000000000000008": { + "balance": "1", + "builtin": { + "name": "alt_bn128_pairing", + "activate_at": 0, + "pricing": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 } } + } + }, + "0000000000000000000000000000000000001337": { + "balance": "1", + "constructor": "0x606060405233600060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550670de0b6b3a764000060035534610000575b612904806100666000396000f3006060604052361561013c576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806306b2ff471461014157806313af40351461018c57806319362a28146101bf5780633f3935d114610248578063432ced04146102b75780634f39ca59146102eb5780636795dbcd1461032457806369fe0e2d146103c857806379ce9fac146103fd5780638da5cb5b1461045557806390b97fc1146104a457806392698814146105245780639890220b1461055d578063ac4e73f914610584578063ac72c12014610612578063c3a358251461064b578063ddca3f43146106c3578063deb931a2146106e6578063df57b74214610747578063e30bd740146107a8578063eadf976014610862578063ef5454d6146108e7578063f25eb5c114610975578063f6d339e414610984575b610000565b3461000057610172600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610a1f565b604051808215151515815260200191505060405180910390f35b34610000576101bd600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610a81565b005b346100005761022e60048080356000191690602001909190803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509190803560001916906020019091905050610ba2565b604051808215151515815260200191505060405180910390f35b346100005761029d600480803590602001908201803590602001908080601f01602080910402602001604051908101604052809392919081815260200183838082843782019150505050505091905050610dc9565b604051808215151515815260200191505060405180910390f35b6102d1600480803560001916906020019091905050611035565b604051808215151515815260200191505060405180910390f35b346100005761030a60048080356000191690602001909190505061115f565b604051808215151515815260200191505060405180910390f35b346100005761038660048080356000191690602001909190803590602001908201803590602001908080601f01602080910402602001604051908101604052809392919081815260200183838082843782019150505050505091905050611378565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34610000576103e3600480803590602001909190505061140d565b604051808215151515815260200191505060405180910390f35b346100005761043b60048080356000191690602001909190803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506114b4565b604051808215151515815260200191505060405180910390f35b34610000576104626115fb565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b346100005761050660048080356000191690602001909190803590602001908201803590602001908080601f01602080910402602001604051908101604052809392919081815260200183838082843782019150505050505091905050611621565b60405180826000191660001916815260200191505060405180910390f35b34610000576105436004808035600019169060200190919050506116b2565b604051808215151515815260200191505060405180910390f35b346100005761056a611715565b604051808215151515815260200191505060405180910390f35b34610000576105f8600480803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509190803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050611824565b604051808215151515815260200191505060405180910390f35b3461000057610631600480803560001916906020019091905050611d8b565b604051808215151515815260200191505060405180910390f35b34610000576106ad60048080356000191690602001909190803590602001908201803590602001908080601f01602080910402602001604051908101604052809392919081815260200183838082843782019150505050505091905050611dee565b6040518082815260200191505060405180910390f35b34610000576106d0611e83565b6040518082815260200191505060405180910390f35b3461000057610705600480803560001916906020019091905050611e89565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b3461000057610766600480803560001916906020019091905050611ed2565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34610000576107d9600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050611f1b565b6040518080602001828103825283818151815260200191508051906020019080838360008314610828575b80518252602083111561082857602082019150602081019050602083039250610804565b505050905090810190601f1680156108545780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34610000576108cd60048080356000191690602001909190803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509190803590602001909190505061200c565b604051808215151515815260200191505060405180910390f35b346100005761095b600480803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509190803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050612236565b604051808215151515815260200191505060405180910390f35b3461000057610982612425565b005b3461000057610a0560048080356000191690602001909190803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509190803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050612698565b604051808215151515815260200191505060405180910390f35b60006000600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020805460018160011615610100020316600290049050141590505b919050565b600060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141515610add57610b9f565b8073ffffffffffffffffffffffffffffffffffffffff16600060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f70aea8d848e8a90fb7661b227dc522eb6395c3dac71b63cb59edd5c9899b236460405180905060405180910390a380600060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505b5b50565b6000833373ffffffffffffffffffffffffffffffffffffffff1660016000836000191660001916815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16141515610c1d57610dc1565b82600160008760001916600019168152602001908152602001600020600201856040518082805190602001908083835b60208310610c705780518252602082019150602081019050602083039250610c4d565b6001836020036101000a03801982511681845116808217855250505050505090500191505090815260200160405180910390208160001916905550836040518082805190602001908083835b60208310610cdf5780518252602082019150602081019050602083039250610cbc565b6001836020036101000a038019825116818451168082178552505050505050905001915050604051809103902085600019167fb829c3e412537bbe794c048ccb9e4605bb4aaaa8e4d4c15c1a6e0c2adc1716ea866040518080602001828103825283818151815260200191508051906020019080838360008314610d82575b805182526020831115610d8257602082019150602081019050602083039250610d5e565b505050905090810190601f168015610dae5780820380516001836020036101000a031916815260200191505b509250505060405180910390a3600191505b5b509392505050565b6000813373ffffffffffffffffffffffffffffffffffffffff1660016000836040518082805190602001908083835b60208310610e1b5780518252602082019150602081019050602083039250610df8565b6001836020036101000a03801982511681845116808217855250505050505090500191505060405180910390206000191660001916815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16141515610ea45761102f565b82600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000209080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10610f2d57805160ff1916838001178555610f5b565b82800160010185558215610f5b579182015b82811115610f5a578251825591602001919060010190610f3f565b5b509050610f8091905b80821115610f7c576000816000905550600101610f64565b5090565b50503373ffffffffffffffffffffffffffffffffffffffff16836040518082805190602001908083835b60208310610fcd5780518252602082019150602081019050602083039250610faa565b6001836020036101000a03801982511681845116808217855250505050505090500191505060405180910390207f098ae8581bb8bd9af1beaf7f2e9f51f31a8e5a8bfada4e303a645d71d9c9192060405180905060405180910390a3600191505b5b50919050565b600081600060016000836000191660001916815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614151561109b57611159565b6003543410156110aa57611158565b3360016000856000191660001916815260200190815260200160002060000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055503373ffffffffffffffffffffffffffffffffffffffff1683600019167f4963513eca575aba66fdcd25f267aae85958fe6fb97e75fa25d783f1a091a22160405180905060405180910390a3600191505b5b5b50919050565b6000813373ffffffffffffffffffffffffffffffffffffffff1660016000836000191660001916815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415156111da57611372565b6002600060016000866000191660001916815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020805460018160011615610100020316600290046000825580601f1061127c57506112b3565b601f0160209004906000526020600020908101906112b291905b808211156112ae576000816000905550600101611296565b5090565b5b5060016000846000191660001916815260200190815260200160002060006000820160006101000a81549073ffffffffffffffffffffffffffffffffffffffff02191690556001820160006101000a81549073ffffffffffffffffffffffffffffffffffffffff021916905550503373ffffffffffffffffffffffffffffffffffffffff1683600019167fef1961b4d2909dc23643b309bfe5c3e5646842d98c3a58517037ef3871185af360405180905060405180910390a3600191505b5b50919050565b6000600160008460001916600019168152602001908152602001600020600201826040518082805190602001908083835b602083106113cc57805182526020820191506020810190506020830392506113a9565b6001836020036101000a0380198251168184511680821785525050505050509050019150509081526020016040518091039020546001900490505b92915050565b6000600060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614151561146b576114af565b816003819055507f6bbc57480a46553fa4d156ce702beef5f3ad66303b0ed1a5d4cb44966c6584c3826040518082815260200191505060405180910390a1600190505b5b919050565b6000823373ffffffffffffffffffffffffffffffffffffffff1660016000836000191660001916815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614151561152f576115f4565b8260016000866000191660001916815260200190815260200160002060000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1685600019167f7b97c62130aa09acbbcbf7482630e756592496f1759eaf702f469cf64dfb779460405180905060405180910390a4600191505b5b5092915050565b600060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000600160008460001916600019168152602001908152602001600020600201826040518082805190602001908083835b602083106116755780518252602082019150602081019050602083039250611652565b6001836020036101000a03801982511681845116808217855250505050505090500191505090815260200160405180910390205490505b92915050565b6000600060016000846000191660001916815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16141590505b919050565b6000600060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614151561177357611821565b7fdef931299fe61d176f949118058530c1f3f539dcb6950b4e372c9b835c33ca073073ffffffffffffffffffffffffffffffffffffffff16316040518082815260200191505060405180910390a13373ffffffffffffffffffffffffffffffffffffffff166108fc3073ffffffffffffffffffffffffffffffffffffffff16319081150290604051809050600060405180830381858888f19350505050151561181b57610000565b600190505b5b90565b60006000836040518082805190602001908083835b6020831061185c5780518252602082019150602081019050602083039250611839565b6001836020036101000a03801982511681845116808217855250505050505090500191505060405180910390203373ffffffffffffffffffffffffffffffffffffffff1660016000836000191660001916815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614151561190157611d83565b846040518082805190602001908083835b602083106119355780518252602082019150602081019050602083039250611912565b6001836020036101000a03801982511681845116808217855250505050505090500191505060405180910390209150600060016000846000191660001916815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614158015611ab4575081600019166002600060016000866000191660001916815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206040518082805460018160011615610100020316600290048015611aa15780601f10611a7f576101008083540402835291820191611aa1565b820191906000526020600020905b815481529060010190602001808311611a8d575b5050915050604051809103902060001916145b15611c79576002600060016000856000191660001916815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020805460018160011615610100020316600290046000825580601f10611b5b5750611b92565b601f016020900490600052602060002090810190611b9191905b80821115611b8d576000816000905550600101611b75565b5090565b5b5060016000836000191660001916815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16856040518082805190602001908083835b60208310611c1c5780518252602082019150602081019050602083039250611bf9565b6001836020036101000a03801982511681845116808217855250505050505090500191505060405180910390207f12491ad95fd945e444d88a894ffad3c21959880a4dcd8af99d4ae4ffc71d4abd60405180905060405180910390a35b8360016000846000191660001916815260200190815260200160002060010160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508373ffffffffffffffffffffffffffffffffffffffff16856040518082805190602001908083835b60208310611d215780518252602082019150602081019050602083039250611cfe565b6001836020036101000a03801982511681845116808217855250505050505090500191505060405180910390207f728435a0031f6a04538fcdd24922a7e06bc7bc945db03e83d22122d1bc5f28df60405180905060405180910390a3600192505b5b505092915050565b6000600060016000846000191660001916815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16141590505b919050565b6000600160008460001916600019168152602001908152602001600020600201826040518082805190602001908083835b60208310611e425780518252602082019150602081019050602083039250611e1f565b6001836020036101000a0380198251168184511680821785525050505050509050019150509081526020016040518091039020546001900490505b92915050565b60035481565b600060016000836000191660001916815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690505b919050565b600060016000836000191660001916815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690505b919050565b6020604051908101604052806000815250600260008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015611fff5780601f10611fd457610100808354040283529160200191611fff565b820191906000526020600020905b815481529060010190602001808311611fe257829003601f168201915b505050505090505b919050565b6000833373ffffffffffffffffffffffffffffffffffffffff1660016000836000191660001916815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415156120875761222e565b82600102600160008760001916600019168152602001908152602001600020600201856040518082805190602001908083835b602083106120dd57805182526020820191506020810190506020830392506120ba565b6001836020036101000a03801982511681845116808217855250505050505090500191505090815260200160405180910390208160001916905550836040518082805190602001908083835b6020831061214c5780518252602082019150602081019050602083039250612129565b6001836020036101000a038019825116818451168082178552505050505050905001915050604051809103902085600019167fb829c3e412537bbe794c048ccb9e4605bb4aaaa8e4d4c15c1a6e0c2adc1716ea8660405180806020018281038252838181518152602001915080519060200190808383600083146121ef575b8051825260208311156121ef576020820191506020810190506020830392506121cb565b505050905090810190601f16801561221b5780820380516001836020036101000a031916815260200191505b509250505060405180910390a3600191505b5b509392505050565b6000600060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161415156122945761241f565b82600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000209080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061231d57805160ff191683800117855561234b565b8280016001018555821561234b579182015b8281111561234a57825182559160200191906001019061232f565b5b50905061237091905b8082111561236c576000816000905550600101612354565b5090565b50508173ffffffffffffffffffffffffffffffffffffffff16836040518082805190602001908083835b602083106123bd578051825260208201915060208101905060208303925061239a565b6001836020036101000a03801982511681845116808217855250505050505090500191505060405180910390207f098ae8581bb8bd9af1beaf7f2e9f51f31a8e5a8bfada4e303a645d71d9c9192060405180905060405180910390a3600190505b5b92915050565b3373ffffffffffffffffffffffffffffffffffffffff16600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060405180828054600181600116156101000203166002900480156124d65780601f106124b45761010080835404028352918201916124d6565b820191906000526020600020905b8154815290600101906020018083116124c2575b505091505060405180910390207f12491ad95fd945e444d88a894ffad3c21959880a4dcd8af99d4ae4ffc71d4abd60405180905060405180910390a360016000600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060405180828054600181600116156101000203166002900480156125b05780601f1061258e5761010080835404028352918201916125b0565b820191906000526020600020905b81548152906001019060200180831161259c575b505091505060405180910390206000191660001916815260200190815260200160002060010160006101000a81549073ffffffffffffffffffffffffffffffffffffffff0219169055600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020805460018160011615610100020316600290046000825580601f1061265d5750612694565b601f01602090049060005260206000209081019061269391905b8082111561268f576000816000905550600101612677565b5090565b5b505b565b6000833373ffffffffffffffffffffffffffffffffffffffff1660016000836000191660001916815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16141515612713576128d0565b8273ffffffffffffffffffffffffffffffffffffffff16600102600160008760001916600019168152602001908152602001600020600201856040518082805190602001908083835b6020831061277f578051825260208201915060208101905060208303925061275c565b6001836020036101000a03801982511681845116808217855250505050505090500191505090815260200160405180910390208160001916905550836040518082805190602001908083835b602083106127ee57805182526020820191506020810190506020830392506127cb565b6001836020036101000a038019825116818451168082178552505050505050905001915050604051809103902085600019167fb829c3e412537bbe794c048ccb9e4605bb4aaaa8e4d4c15c1a6e0c2adc1716ea866040518080602001828103825283818151815260200191508051906020019080838360008314612891575b8051825260208311156128915760208201915060208101905060208303925061286d565b505050905090810190601f1680156128bd5780820380516001836020036101000a031916815260200191505b509250505060405180910390a3600191505b5b5093925050505600a165627a7a7230582066b2da4773a0f1d81efe071c66b51c46868a871661efd18c0f629353ff4c1f9b0029" + }, + "aaB52d66283F7A1D5978bcFcB55721ACB467384b": { + "balance": "1606938044258990275541962092341162602522202993782792835301376" + }, + "bb140FbA6242a1c3887A7823F7750a73101383e3": { + "balance": "1606938044258990275541962092341162602522202993782792835301376" + }, + "7FC1442AB55Da569940Eb750AaD2BAA63DA4010E": { "balance": "500000000000000000000" }, + "B4579fd5AfEaB60B03Db3F408AAdD315035943f7": { "balance": "500000000000000000000" }, + "89d24A6b4CcB1B6fAA2625fE562bDD9a23260359": { + "balance": "0", + "code": "0x6080604052600436106100fb5763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166305d2035b811461010057806306fdde0314610129578063095ea7b3146101b357806318160ddd146101d757806323b872dd146101fe578063313ce5671461022857806340c10f191461025357806342966c6814610277578063661884631461029157806370a08231146102b5578063715018a6146102d65780637d64bcb4146102eb5780638da5cb5b1461030057806395d89b4114610331578063a9059cbb14610346578063d73dd6231461036a578063dd62ed3e1461038e578063f2fde38b146103b5575b600080fd5b34801561010c57600080fd5b506101156103d6565b604080519115158252519081900360200190f35b34801561013557600080fd5b5061013e6103f7565b6040805160208082528351818301528351919283929083019185019080838360005b83811015610178578181015183820152602001610160565b50505050905090810190601f1680156101a55780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b3480156101bf57600080fd5b50610115600160a060020a0360043516602435610485565b3480156101e357600080fd5b506101ec6104eb565b60408051918252519081900360200190f35b34801561020a57600080fd5b50610115600160a060020a03600435811690602435166044356104f1565b34801561023457600080fd5b5061023d610656565b6040805160ff9092168252519081900360200190f35b34801561025f57600080fd5b50610115600160a060020a036004351660243561065f565b34801561028357600080fd5b5061028f60043561076a565b005b34801561029d57600080fd5b50610115600160a060020a0360043516602435610777565b3480156102c157600080fd5b506101ec600160a060020a0360043516610866565b3480156102e257600080fd5b5061028f610881565b3480156102f757600080fd5b506101156108ef565b34801561030c57600080fd5b50610315610995565b60408051600160a060020a039092168252519081900360200190f35b34801561033d57600080fd5b5061013e6109a4565b34801561035257600080fd5b50610115600160a060020a03600435166024356109fe565b34801561037657600080fd5b50610115600160a060020a0360043516602435610acd565b34801561039a57600080fd5b506101ec600160a060020a0360043581169060243516610b66565b3480156103c157600080fd5b5061028f600160a060020a0360043516610b91565b60065474010000000000000000000000000000000000000000900460ff1681565b6000805460408051602060026001851615610100026000190190941693909304601f8101849004840282018401909252818152929183018282801561047d5780601f106104525761010080835404028352916020019161047d565b820191906000526020600020905b81548152906001019060200180831161046057829003601f168201915b505050505081565b336000818152600560209081526040808320600160a060020a038716808552908352818420869055815186815291519394909390927f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925928290030190a350600192915050565b60045490565b600160a060020a03831660009081526003602052604081205482111561051657600080fd5b600160a060020a038416600090815260056020908152604080832033845290915290205482111561054657600080fd5b600160a060020a038316151561055b57600080fd5b600160a060020a038416600090815260036020526040902054610584908363ffffffff610bb116565b600160a060020a0380861660009081526003602052604080822093909355908516815220546105b9908363ffffffff610bc316565b600160a060020a0380851660009081526003602090815260408083209490945591871681526005825282812033825290915220546105fd908363ffffffff610bb116565b600160a060020a0380861660008181526005602090815260408083203384528252918290209490945580518681529051928716939192600080516020610d44833981519152929181900390910190a35060019392505050565b60025460ff1681565b600654600090600160a060020a0316331461067957600080fd5b60065474010000000000000000000000000000000000000000900460ff16156106a157600080fd5b6004546106b4908363ffffffff610bc316565b600455600160a060020a0383166000908152600360205260409020546106e0908363ffffffff610bc316565b600160a060020a038416600081815260036020908152604091829020939093558051858152905191927f0f6798a560793a54c3bcfe86a93cde1e73087d944c0ea20544137d412139688592918290030190a2604080518381529051600160a060020a03851691600091600080516020610d448339815191529181900360200190a350600192915050565b6107743382610bd6565b50565b336000908152600560209081526040808320600160a060020a03861684529091528120548083106107cb57336000908152600560209081526040808320600160a060020a0388168452909152812055610800565b6107db818463ffffffff610bb116565b336000908152600560209081526040808320600160a060020a03891684529091529020555b336000818152600560209081526040808320600160a060020a0389168085529083529281902054815190815290519293927f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925929181900390910190a35060019392505050565b600160a060020a031660009081526003602052604090205490565b600654600160a060020a0316331461089857600080fd5b600654604051600160a060020a03909116907ff8df31144d9c2f0f6b59d69b8b98abd5459d07f2742c4df920b25aae33c6482090600090a26006805473ffffffffffffffffffffffffffffffffffffffff19169055565b600654600090600160a060020a0316331461090957600080fd5b60065474010000000000000000000000000000000000000000900460ff161561093157600080fd5b6006805474ff00000000000000000000000000000000000000001916740100000000000000000000000000000000000000001790556040517fae5184fba832cb2b1f702aca6117b8d265eaf03ad33eb133f19dde0f5920fa0890600090a150600190565b600654600160a060020a031681565b60018054604080516020600284861615610100026000190190941693909304601f8101849004840282018401909252818152929183018282801561047d5780601f106104525761010080835404028352916020019161047d565b33600090815260036020526040812054821115610a1a57600080fd5b600160a060020a0383161515610a2f57600080fd5b33600090815260036020526040902054610a4f908363ffffffff610bb116565b3360009081526003602052604080822092909255600160a060020a03851681522054610a81908363ffffffff610bc316565b600160a060020a038416600081815260036020908152604091829020939093558051858152905191923392600080516020610d448339815191529281900390910190a350600192915050565b336000908152600560209081526040808320600160a060020a0386168452909152812054610b01908363ffffffff610bc316565b336000818152600560209081526040808320600160a060020a0389168085529083529281902085905580519485525191937f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925929081900390910190a350600192915050565b600160a060020a03918216600090815260056020908152604080832093909416825291909152205490565b600654600160a060020a03163314610ba857600080fd5b61077481610cc5565b600082821115610bbd57fe5b50900390565b81810182811015610bd057fe5b92915050565b600160a060020a038216600090815260036020526040902054811115610bfb57600080fd5b600160a060020a038216600090815260036020526040902054610c24908263ffffffff610bb116565b600160a060020a038316600090815260036020526040902055600454610c50908263ffffffff610bb116565b600455604080518281529051600160a060020a038416917fcc16f5dbb4873280815c1ee09dbd06736cffcc184412cf7a71a0fdb75d397ca5919081900360200190a2604080518281529051600091600160a060020a03851691600080516020610d448339815191529181900360200190a35050565b600160a060020a0381161515610cda57600080fd5b600654604051600160a060020a038084169216907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a36006805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a03929092169190911790555600ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa165627a7a72305820d0e5d0b7b0df4e2a06bb4d81bf6574bb34947120791873e3eccd4bf676f7b9f30029", + "storage": { + "0x0": "0x7361690000000000000000000000000000000000000000000000000000000006", + "0x1": "0x5341490000000000000000000000000000000000000000000000000000000006", + "0x2": "0x12", + "0x4": "0x00000000000000000000000000000000000000000000001043561a8829300000", + "0x6": "0x379a38a225fadb4a3769391ab26e2440681bf098", + "0xe72abea4d7a02a1dd3bab9806f38066f472bc02778742ed50ed69de48da9c3e6" : "0x0000000000000000000000000000000000000000000000056bc75e2d63100000", + "0xc19ddbb1e237d15aa362708548b7cbc089c94e4977af9e54c2f09f0ae28a35fc" : "0x0000000000000000000000000000000000000000000000056bc75e2d63100000", + "0xca2b12ce800b15ade60b8e814bbbf66b91734af718ac5562e6b8adc9b4e5b629" : "0x0000000000000000000000000000000000000000000000056bc75e2d63100000" + } + }, + "7cc4b1851c35959d34e635a470f6b5c43ba3c9c9": { + "balance": "0", + "code": "0x6080604052600436106100fb5763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166305d2035b811461010057806306fdde0314610129578063095ea7b3146101b357806318160ddd146101d757806323b872dd146101fe578063313ce5671461022857806340c10f191461025357806342966c6814610277578063661884631461029157806370a08231146102b5578063715018a6146102d65780637d64bcb4146102eb5780638da5cb5b1461030057806395d89b4114610331578063a9059cbb14610346578063d73dd6231461036a578063dd62ed3e1461038e578063f2fde38b146103b5575b600080fd5b34801561010c57600080fd5b506101156103d6565b604080519115158252519081900360200190f35b34801561013557600080fd5b5061013e6103f7565b6040805160208082528351818301528351919283929083019185019080838360005b83811015610178578181015183820152602001610160565b50505050905090810190601f1680156101a55780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b3480156101bf57600080fd5b50610115600160a060020a0360043516602435610485565b3480156101e357600080fd5b506101ec6104eb565b60408051918252519081900360200190f35b34801561020a57600080fd5b50610115600160a060020a03600435811690602435166044356104f1565b34801561023457600080fd5b5061023d610656565b6040805160ff9092168252519081900360200190f35b34801561025f57600080fd5b50610115600160a060020a036004351660243561065f565b34801561028357600080fd5b5061028f60043561076a565b005b34801561029d57600080fd5b50610115600160a060020a0360043516602435610777565b3480156102c157600080fd5b506101ec600160a060020a0360043516610866565b3480156102e257600080fd5b5061028f610881565b3480156102f757600080fd5b506101156108ef565b34801561030c57600080fd5b50610315610995565b60408051600160a060020a039092168252519081900360200190f35b34801561033d57600080fd5b5061013e6109a4565b34801561035257600080fd5b50610115600160a060020a03600435166024356109fe565b34801561037657600080fd5b50610115600160a060020a0360043516602435610acd565b34801561039a57600080fd5b506101ec600160a060020a0360043581169060243516610b66565b3480156103c157600080fd5b5061028f600160a060020a0360043516610b91565b60065474010000000000000000000000000000000000000000900460ff1681565b6000805460408051602060026001851615610100026000190190941693909304601f8101849004840282018401909252818152929183018282801561047d5780601f106104525761010080835404028352916020019161047d565b820191906000526020600020905b81548152906001019060200180831161046057829003601f168201915b505050505081565b336000818152600560209081526040808320600160a060020a038716808552908352818420869055815186815291519394909390927f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925928290030190a350600192915050565b60045490565b600160a060020a03831660009081526003602052604081205482111561051657600080fd5b600160a060020a038416600090815260056020908152604080832033845290915290205482111561054657600080fd5b600160a060020a038316151561055b57600080fd5b600160a060020a038416600090815260036020526040902054610584908363ffffffff610bb116565b600160a060020a0380861660009081526003602052604080822093909355908516815220546105b9908363ffffffff610bc316565b600160a060020a0380851660009081526003602090815260408083209490945591871681526005825282812033825290915220546105fd908363ffffffff610bb116565b600160a060020a0380861660008181526005602090815260408083203384528252918290209490945580518681529051928716939192600080516020610d44833981519152929181900390910190a35060019392505050565b60025460ff1681565b600654600090600160a060020a0316331461067957600080fd5b60065474010000000000000000000000000000000000000000900460ff16156106a157600080fd5b6004546106b4908363ffffffff610bc316565b600455600160a060020a0383166000908152600360205260409020546106e0908363ffffffff610bc316565b600160a060020a038416600081815260036020908152604091829020939093558051858152905191927f0f6798a560793a54c3bcfe86a93cde1e73087d944c0ea20544137d412139688592918290030190a2604080518381529051600160a060020a03851691600091600080516020610d448339815191529181900360200190a350600192915050565b6107743382610bd6565b50565b336000908152600560209081526040808320600160a060020a03861684529091528120548083106107cb57336000908152600560209081526040808320600160a060020a0388168452909152812055610800565b6107db818463ffffffff610bb116565b336000908152600560209081526040808320600160a060020a03891684529091529020555b336000818152600560209081526040808320600160a060020a0389168085529083529281902054815190815290519293927f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925929181900390910190a35060019392505050565b600160a060020a031660009081526003602052604090205490565b600654600160a060020a0316331461089857600080fd5b600654604051600160a060020a03909116907ff8df31144d9c2f0f6b59d69b8b98abd5459d07f2742c4df920b25aae33c6482090600090a26006805473ffffffffffffffffffffffffffffffffffffffff19169055565b600654600090600160a060020a0316331461090957600080fd5b60065474010000000000000000000000000000000000000000900460ff161561093157600080fd5b6006805474ff00000000000000000000000000000000000000001916740100000000000000000000000000000000000000001790556040517fae5184fba832cb2b1f702aca6117b8d265eaf03ad33eb133f19dde0f5920fa0890600090a150600190565b600654600160a060020a031681565b60018054604080516020600284861615610100026000190190941693909304601f8101849004840282018401909252818152929183018282801561047d5780601f106104525761010080835404028352916020019161047d565b33600090815260036020526040812054821115610a1a57600080fd5b600160a060020a0383161515610a2f57600080fd5b33600090815260036020526040902054610a4f908363ffffffff610bb116565b3360009081526003602052604080822092909255600160a060020a03851681522054610a81908363ffffffff610bc316565b600160a060020a038416600081815260036020908152604091829020939093558051858152905191923392600080516020610d448339815191529281900390910190a350600192915050565b336000908152600560209081526040808320600160a060020a0386168452909152812054610b01908363ffffffff610bc316565b336000818152600560209081526040808320600160a060020a0389168085529083529281902085905580519485525191937f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925929081900390910190a350600192915050565b600160a060020a03918216600090815260056020908152604080832093909416825291909152205490565b600654600160a060020a03163314610ba857600080fd5b61077481610cc5565b600082821115610bbd57fe5b50900390565b81810182811015610bd057fe5b92915050565b600160a060020a038216600090815260036020526040902054811115610bfb57600080fd5b600160a060020a038216600090815260036020526040902054610c24908263ffffffff610bb116565b600160a060020a038316600090815260036020526040902055600454610c50908263ffffffff610bb116565b600455604080518281529051600160a060020a038416917fcc16f5dbb4873280815c1ee09dbd06736cffcc184412cf7a71a0fdb75d397ca5919081900360200190a2604080518281529051600091600160a060020a03851691600080516020610d448339815191529181900360200190a35050565b600160a060020a0381161515610cda57600080fd5b600654604051600160a060020a038084169216907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a36006805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a03929092169190911790555600ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa165627a7a72305820d0e5d0b7b0df4e2a06bb4d81bf6574bb34947120791873e3eccd4bf676f7b9f30029", + "storage": { + "0x0": "0x6461690000000000000000000000000000000000000000000000000000000006", + "0x1": "0x4441490000000000000000000000000000000000000000000000000000000006", + "0x2": "0x12", + "0x4": "0x00000000000000000000000000000000000000000000001043561a8829300000", + "0x6": "0xc73e0383F3Aff3215E6f04B0331D58CeCf0Ab849", + "0xe72abea4d7a02a1dd3bab9806f38066f472bc02778742ed50ed69de48da9c3e6" : "0x0000000000000000000000000000000000000000000000056bc75e2d63100000", + "0xc19ddbb1e237d15aa362708548b7cbc089c94e4977af9e54c2f09f0ae28a35fc" : "0x0000000000000000000000000000000000000000000000056bc75e2d63100000", + "0xca2b12ce800b15ade60b8e814bbbf66b91734af718ac5562e6b8adc9b4e5b629" : "0x0000000000000000000000000000000000000000000000056bc75e2d63100000" + } + }, + "9b0ccf7C8994E19F39b2B4CF708e0A7DF65fA8a3": { + "balance": "0", + "code": "0x60806040526004361060485763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166375deea058114604d578063f83e5793146064575b600080fd5b348015605857600080fd5b5060626004356088565b005b348015606f57600080fd5b506076608d565b60408051918252519081900360200190f35b600055565b600054815600a165627a7a723058208e2285b1eeae32758b281704fcc08389b6c63f59ede491ae6a1a8609872e5a660029" + }, + "c4c7497fbe1a886841a195a5d622cd60053c1376": { + "balance": "0", + "code": "0x608060405260043610603e5763ffffffff7c0100000000000000000000000000000000000000000000000000000000600035041663f4b9fa7581146043575b600080fd5b348015604e57600080fd5b506055607e565b6040805173ffffffffffffffffffffffffffffffffffffffff9092168252519081900360200190f35b60005473ffffffffffffffffffffffffffffffffffffffff16815600a165627a7a72305820cb562b65332a4716fd33d1bd38bced0faee567813159da4c21958b3d161ac5d00029", + "storage": { + "0x0": "0x7cc4b1851c35959d34e635a470f6b5c43ba3c9c9" + } + }, + "c73e0383F3Aff3215E6f04B0331D58CeCf0Ab849": { + "balance": "0", + "code": "0x6080604052600436106100565763ffffffff7c01000000000000000000000000000000000000000000000000000000006000350416639166cba4811461005b578063c11645bc14610099578063fbabdebd146100ae575b600080fd5b34801561006757600080fd5b506100706100c8565b6040805173ffffffffffffffffffffffffffffffffffffffff9092168252519081900360200190f35b3480156100a557600080fd5b506100706100e4565b3480156100ba57600080fd5b506100c6600435610100565b005b60005473ffffffffffffffffffffffffffffffffffffffff1681565b60015473ffffffffffffffffffffffffffffffffffffffff1690565b60008054604080517f23b872dd00000000000000000000000000000000000000000000000000000000815233600482015230602482015260448101859052905173ffffffffffffffffffffffffffffffffffffffff909216926323b872dd926064808401936020939083900390910190829087803b15801561018157600080fd5b505af1158015610195573d6000803e3d6000fd5b505050506040513d60208110156101ab57600080fd5b5050600154604080517ff4b9fa75000000000000000000000000000000000000000000000000000000008152905173ffffffffffffffffffffffffffffffffffffffff9092169163f4b9fa75916004808201926020929091908290030181600087803b15801561021a57600080fd5b505af115801561022e573d6000803e3d6000fd5b505050506040513d602081101561024457600080fd5b5051604080517f40c10f1900000000000000000000000000000000000000000000000000000000815233600482015260248101849052905173ffffffffffffffffffffffffffffffffffffffff909216916340c10f19916044808201926020929091908290030181600087803b1580156102bd57600080fd5b505af11580156102d1573d6000803e3d6000fd5b505050506040513d60208110156102e757600080fd5b5050505600a165627a7a7230582009c5ceaf186502ed1263893e36ef7d8cd7712822fbaae70843d55a7d5c686fc60029", + "storage": { + "0x0": "0x89d24A6b4CcB1B6fAA2625fE562bDD9a23260359", + "0x1": "0xc4c7497fbe1a886841a195a5d622cd60053c1376" + } + } + } }