Merge the develop branch to the master branch, preparation to v1.0.0 #230
* Support alternative receiver in Oracle (#221) * Support alternative receiver feature in Monitor (#223) * Support token migration (#224) * Fix suggested gas price in transaction for ui production build (#222) * Updated links to new repo with tokenbridge contracts (#226) * Update the contract's submodule to the release 3.2.0 (#228)
This commit is contained in:
commit
db89d1c12e
2
.gitmodules
vendored
2
.gitmodules
vendored
@ -1,3 +1,3 @@
|
||||
[submodule "contracts"]
|
||||
path = contracts
|
||||
url = https://github.com/poanetwork/poa-bridge-contracts.git
|
||||
url = https://github.com/poanetwork/tokenbridge-contracts.git
|
||||
|
@ -30,7 +30,7 @@ Sub-repositories maintained within this monorepo are listed below.
|
||||
| [Commons](commons/README.md) | Interfaces, constants and utilities shared between the sub-repositories |
|
||||
| [E2E-Commons](e2e-commons/README.md) | Common utilities and configuration used in end to end tests |
|
||||
|
||||
Additionally there are [Smart Contracts](https://github.com/poanetwork/poa-bridge-contracts) used to manage bridge validators, collect signatures, and confirm asset relay and disposal.
|
||||
Additionally there are [Smart Contracts](https://github.com/poanetwork/tokenbridge-contracts) used to manage bridge validators, collect signatures, and confirm asset relay and disposal.
|
||||
|
||||
## Available deployments
|
||||
|
||||
|
@ -17,8 +17,11 @@ const FEE_MANAGER_MODE = {
|
||||
UNDEFINED: 'UNDEFINED'
|
||||
}
|
||||
|
||||
const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000'
|
||||
|
||||
module.exports = {
|
||||
BRIDGE_MODES,
|
||||
ERC_TYPES,
|
||||
FEE_MANAGER_MODE
|
||||
FEE_MANAGER_MODE,
|
||||
ZERO_ADDRESS
|
||||
}
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit 86b35f8382d5cb98e5738128a0c1a3718c471c72
|
||||
Subproject commit 20d262702defd3aed39870f3379f1cf0c314305e
|
@ -32,5 +32,31 @@
|
||||
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"
|
||||
|
||||
- name: start the service
|
||||
shell: service poabridge start
|
||||
|
@ -25,6 +25,21 @@
|
||||
set_fact:
|
||||
ORACLE_VALIDATOR_ADDRESS: "{{ VADDRESS.stdout }}"
|
||||
|
||||
- name: Get foreign erc type
|
||||
become_user: "{{ compose_service_user }}"
|
||||
shell: docker-compose run --entrypoint "node scripts/initialChecks.js" bridge_affirmation
|
||||
args:
|
||||
chdir: "{{ bridge_path }}/oracle"
|
||||
register: ERCTYPE
|
||||
|
||||
- name: Set FOREIGN_ERC_TYPE variable
|
||||
set_fact:
|
||||
FOREIGN_ERC_TYPE: "{{ (ERCTYPE.stdout).foreignERC | default('') }}"
|
||||
|
||||
- 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")
|
||||
|
||||
- name: Install .key config
|
||||
template:
|
||||
src: key.j2
|
||||
|
@ -16,6 +16,7 @@ WORKDIR="{{ '/home/' + compose_service_user | default('poadocker') + '/' + bridg
|
||||
keyfile="{{ keyfile_path }}"
|
||||
vaddr="ORACLE_VALIDATOR_ADDRESS="
|
||||
vkey="ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY="
|
||||
composefileoverride="{{ composefileoverride | default('') }}"
|
||||
|
||||
#Parsing file content and add key to variable
|
||||
while read -r line
|
||||
@ -33,30 +34,30 @@ done < $keyfile
|
||||
start(){
|
||||
echo "Starting bridge.."
|
||||
cd $WORKDIR
|
||||
sudo -u "{{ compose_service_user }}" /usr/local/bin/docker-compose down -v
|
||||
sudo -u "{{ compose_service_user }}" /usr/local/bin/docker-compose rm -fv
|
||||
sudo -u "{{ compose_service_user }}" ORACLE_VALIDATOR_ADDRESS=$vaddr ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY=$vkey /usr/local/bin/docker-compose up --detach
|
||||
sudo -u "{{ compose_service_user }}" /usr/local/bin/docker-compose $composefileoverride down -v
|
||||
sudo -u "{{ compose_service_user }}" /usr/local/bin/docker-compose $composefileoverride rm -fv
|
||||
sudo -u "{{ compose_service_user }}" ORACLE_VALIDATOR_ADDRESS=$vaddr ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY=$vkey /usr/local/bin/docker-compose $composefileoverride up --detach
|
||||
}
|
||||
|
||||
stop(){
|
||||
echo "Stopping bridge.."
|
||||
cd $WORKDIR
|
||||
sudo -u "{{ compose_service_user }}" /usr/local/bin/docker-compose down -v
|
||||
sudo -u "{{ compose_service_user }}" /usr/local/bin/docker-compose $composefileoverride down -v
|
||||
sleep 2
|
||||
}
|
||||
|
||||
status(){
|
||||
echo "Bridge status:"
|
||||
cd $WORKDIR
|
||||
sudo -u "{{ compose_service_user }}" /usr/local/bin/docker-compose ps
|
||||
sudo -u "{{ compose_service_user }}" /usr/local/bin/docker-compose $composefileoverride ps
|
||||
}
|
||||
|
||||
rebuild(){
|
||||
echo "Rebuild bridge.."
|
||||
cd $WORKDIR
|
||||
sudo -u "{{ compose_service_user }}" /usr/local/bin/docker-compose down -v
|
||||
sudo -u "{{ compose_service_user }}" /usr/local/bin/docker-compose rm -fv
|
||||
sudo -u "{{ compose_service_user }}" ORACLE_VALIDATOR_ADDRESS=$vaddr ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY=$vkey /usr/local/bin/docker-compose up --detach --force-recreate --no-deps --build
|
||||
sudo -u "{{ compose_service_user }}" /usr/local/bin/docker-compose $composefileoverride down -v
|
||||
sudo -u "{{ compose_service_user }}" /usr/local/bin/docker-compose $composefileoverride rm -fv
|
||||
sudo -u "{{ compose_service_user }}" ORACLE_VALIDATOR_ADDRESS=$vaddr ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY=$vkey /usr/local/bin/docker-compose $composefileoverride up --detach --force-recreate --no-deps --build
|
||||
}
|
||||
|
||||
|
||||
|
@ -17,9 +17,11 @@ while [ "$1" != "" ]; do
|
||||
docker-compose run -d oracle-erc20 yarn watcher:signature-request
|
||||
docker-compose run -d oracle-erc20 yarn watcher:collected-signatures
|
||||
docker-compose run -d oracle-erc20 yarn watcher:affirmation-request
|
||||
docker-compose run -d oracle-erc20 yarn watcher:transfer
|
||||
docker-compose run -d oracle-erc20-native yarn watcher:signature-request
|
||||
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-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
|
||||
|
@ -8,5 +8,8 @@
|
||||
"rules": {
|
||||
"no-use-before-define": "off",
|
||||
"node/no-unpublished-require": "off"
|
||||
},
|
||||
"env": {
|
||||
"mocha": true
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ const Web3 = require('web3')
|
||||
const logger = require('./logger')('alerts')
|
||||
const eventsInfo = require('./utils/events')
|
||||
const { getBlockNumber } = require('./utils/contract')
|
||||
const { processedMsgNotDelivered } = require('./utils/message')
|
||||
const { processedMsgNotDelivered, eventWithoutReference } = require('./utils/message')
|
||||
const { BRIDGE_MODES } = require('../commons')
|
||||
|
||||
const { COMMON_HOME_RPC_URL, COMMON_FOREIGN_RPC_URL } = process.env
|
||||
@ -29,10 +29,9 @@ async function main() {
|
||||
xSignatures = homeToForeignConfirmations.filter(processedMsgNotDelivered(homeToForeignRequests))
|
||||
xAffirmations = foreignToHomeConfirmations.filter(processedMsgNotDelivered(foreignToHomeRequests))
|
||||
} else {
|
||||
xSignatures = homeToForeignConfirmations.filter(findDifferences(homeToForeignRequests))
|
||||
xAffirmations = foreignToHomeConfirmations.filter(findDifferences(foreignToHomeRequests))
|
||||
xSignatures = homeToForeignConfirmations.filter(eventWithoutReference(homeToForeignRequests))
|
||||
xAffirmations = foreignToHomeConfirmations.filter(eventWithoutReference(foreignToHomeRequests))
|
||||
}
|
||||
|
||||
logger.debug('building misbehavior blocks')
|
||||
const [homeBlockNumber, foreignBlockNumber] = await getBlockNumber(web3Home, web3Foreign)
|
||||
|
||||
@ -52,8 +51,8 @@ async function main() {
|
||||
const foreignValidators = await Promise.all(xSignatures.map(event => findTxSender(web3Foreign)(event)))
|
||||
const homeValidators = await Promise.all(xAffirmations.map(event => findTxSender(web3Home)(event)))
|
||||
|
||||
const xSignaturesTxs = xSignatures.map(normalizeEventInformation).reduce(buildTxList(foreignValidators), {})
|
||||
const xAffirmationsTxs = xAffirmations.map(normalizeEventInformation).reduce(buildTxList(homeValidators), {})
|
||||
const xSignaturesTxs = xSignatures.reduce(buildTxList(foreignValidators), {})
|
||||
const xAffirmationsTxs = xAffirmations.reduce(buildTxList(homeValidators), {})
|
||||
|
||||
logger.debug('Done')
|
||||
|
||||
@ -150,7 +149,7 @@ const findTxSender = web3 => async ({ transactionHash }) => {
|
||||
* }}}
|
||||
*/
|
||||
const buildTxList = validatorsList => (acc, event, index) => {
|
||||
acc[event.txHash] = {
|
||||
acc[event.transactionHash] = {
|
||||
value: event.value,
|
||||
block: event.blockNumber,
|
||||
referenceTx: event.referenceTx,
|
||||
@ -160,38 +159,4 @@ const buildTxList = validatorsList => (acc, event, index) => {
|
||||
return acc
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a missing destDeposit in src list if there's any
|
||||
* @param {Array} src
|
||||
* @returns {function(*=): boolean}
|
||||
*/
|
||||
const findDifferences = src => dest => {
|
||||
const b = normalizeEventInformation(dest)
|
||||
|
||||
return (
|
||||
src
|
||||
.map(normalizeEventInformation)
|
||||
.filter(a => a.referenceTx === b.referenceTx && a.recipient === b.recipient && a.value === b.value).length === 0
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes the different event objects to facilitate data processing
|
||||
* @param {Object} event
|
||||
* @returns {{
|
||||
* txHash: string,
|
||||
* blockNumber: number,
|
||||
* referenceTx: string,
|
||||
* recipient: string | *,
|
||||
* value: *
|
||||
* }}
|
||||
*/
|
||||
const normalizeEventInformation = event => ({
|
||||
txHash: event.transactionHash,
|
||||
blockNumber: event.blockNumber,
|
||||
referenceTx: event.returnValues.transactionHash || event.transactionHash,
|
||||
recipient: event.returnValues.recipient || event.returnValues.from,
|
||||
value: event.returnValues.value
|
||||
})
|
||||
|
||||
module.exports = main
|
||||
|
@ -1,69 +1,14 @@
|
||||
require('dotenv').config()
|
||||
const eventsInfo = require('./utils/events')
|
||||
const { processedMsgNotDelivered, deliveredMsgNotProcessed } = require('./utils/message')
|
||||
const { processedMsgNotDelivered, deliveredMsgNotProcessed, eventWithoutReference } = require('./utils/message')
|
||||
const { BRIDGE_MODES } = require('../commons')
|
||||
|
||||
function compareDepositsHome(foreign) {
|
||||
return homeDeposit => {
|
||||
return (
|
||||
foreign.filter(foreignDeposit => {
|
||||
return (
|
||||
foreignDeposit.returnValues.transactionHash === homeDeposit.transactionHash &&
|
||||
foreignDeposit.returnValues.recipient === homeDeposit.returnValues.recipient &&
|
||||
foreignDeposit.returnValues.value === homeDeposit.returnValues.value
|
||||
)
|
||||
}).length === 0
|
||||
)
|
||||
}
|
||||
}
|
||||
function compareDepositsForeign(home) {
|
||||
return foreignDeposit => {
|
||||
return (
|
||||
home.filter(homeDeposit => {
|
||||
return (
|
||||
homeDeposit.transactionHash === foreignDeposit.returnValues.transactionHash &&
|
||||
homeDeposit.returnValues.recipient === foreignDeposit.returnValues.recipient &&
|
||||
homeDeposit.returnValues.value === foreignDeposit.returnValues.value
|
||||
)
|
||||
}).length === 0
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
function compareTransferHome(foreign) {
|
||||
return homeDeposit => {
|
||||
return (
|
||||
foreign.filter(foreignDeposit => {
|
||||
return (
|
||||
homeDeposit.returnValues.transactionHash === foreignDeposit.transactionHash &&
|
||||
homeDeposit.returnValues.recipient === foreignDeposit.returnValues.from &&
|
||||
homeDeposit.returnValues.value === foreignDeposit.returnValues.value
|
||||
)
|
||||
}).length === 0
|
||||
)
|
||||
}
|
||||
}
|
||||
function compareTransferForeign(home) {
|
||||
return foreignDeposit => {
|
||||
return (
|
||||
home.filter(homeDeposit => {
|
||||
return (
|
||||
foreignDeposit.transactionHash === homeDeposit.returnValues.transactionHash &&
|
||||
foreignDeposit.returnValues.from === homeDeposit.returnValues.recipient &&
|
||||
foreignDeposit.returnValues.value === homeDeposit.returnValues.value
|
||||
)
|
||||
}).length === 0
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const {
|
||||
homeToForeignRequests,
|
||||
homeToForeignConfirmations,
|
||||
foreignToHomeConfirmations,
|
||||
foreignToHomeRequests,
|
||||
isExternalErc20,
|
||||
bridgeMode
|
||||
} = await eventsInfo()
|
||||
|
||||
@ -88,17 +33,11 @@ async function main() {
|
||||
lastChecked: Math.floor(Date.now() / 1000)
|
||||
}
|
||||
} else {
|
||||
const onlyInHomeDeposits = homeToForeignRequests.filter(compareDepositsHome(homeToForeignConfirmations))
|
||||
const onlyInForeignDeposits = homeToForeignConfirmations
|
||||
.concat([])
|
||||
.filter(compareDepositsForeign(homeToForeignRequests))
|
||||
const onlyInHomeDeposits = homeToForeignRequests.filter(eventWithoutReference(homeToForeignConfirmations))
|
||||
const onlyInForeignDeposits = homeToForeignConfirmations.filter(eventWithoutReference(homeToForeignRequests))
|
||||
|
||||
const onlyInHomeWithdrawals = isExternalErc20
|
||||
? foreignToHomeConfirmations.filter(compareTransferHome(foreignToHomeRequests))
|
||||
: foreignToHomeConfirmations.filter(compareDepositsForeign(foreignToHomeRequests))
|
||||
const onlyInForeignWithdrawals = isExternalErc20
|
||||
? foreignToHomeRequests.filter(compareTransferForeign(foreignToHomeConfirmations))
|
||||
: foreignToHomeRequests.filter(compareDepositsHome(foreignToHomeConfirmations))
|
||||
const onlyInHomeWithdrawals = foreignToHomeConfirmations.filter(eventWithoutReference(foreignToHomeRequests))
|
||||
const onlyInForeignWithdrawals = foreignToHomeRequests.filter(eventWithoutReference(foreignToHomeConfirmations))
|
||||
|
||||
return {
|
||||
onlyInHomeDeposits,
|
||||
|
@ -8,7 +8,8 @@
|
||||
"start": "node index.js",
|
||||
"check-and-start": "yarn check-all && yarn start",
|
||||
"lint": "eslint . --ignore-path ../.eslintignore",
|
||||
"lint:fix": "eslint . --fix"
|
||||
"lint:fix": "eslint . --fix",
|
||||
"test": "NODE_ENV=test mocha"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
@ -23,5 +24,6 @@
|
||||
"node": ">=8.9"
|
||||
},
|
||||
"devDependencies": {
|
||||
"chai": "^4.2.0"
|
||||
}
|
||||
}
|
||||
|
206
monitor/test/message.test.js
Normal file
206
monitor/test/message.test.js
Normal file
@ -0,0 +1,206 @@
|
||||
const { expect } = require('chai')
|
||||
const { normalizeEventInformation, eventWithoutReference } = require('../utils/message')
|
||||
|
||||
describe('normalizeEventInformation', () => {
|
||||
it('should return normalized object for UserRequestForSignature event', () => {
|
||||
// Given
|
||||
const event = {
|
||||
address: '0x7301CFA0e1756B71869E93d4e4Dca5c7d0eb0AA6',
|
||||
blockNumber: 324231,
|
||||
transactionHash: '0x17be1e0745136b9e2857124542f8218812db8fe4458236d5ae045c1ceeb79978',
|
||||
returnValues: {
|
||||
recipient: '0xA84944735B66e957Fe385567dcc85975022Fe68A',
|
||||
value: '100000000000000000000'
|
||||
},
|
||||
event: 'UserRequestForSignature'
|
||||
}
|
||||
|
||||
// When
|
||||
const result = normalizeEventInformation(event)
|
||||
|
||||
// Then
|
||||
expect(result.transactionHash).to.equal(event.transactionHash)
|
||||
expect(result.blockNumber).to.equal(event.blockNumber)
|
||||
expect(result.referenceTx).to.equal(event.transactionHash)
|
||||
expect(result.recipient).to.equal(event.returnValues.recipient)
|
||||
expect(result.value).to.equal(event.returnValues.value)
|
||||
})
|
||||
it('should return normalized object for UserRequestForAffirmation event', () => {
|
||||
// Given
|
||||
const event = {
|
||||
address: '0x7301CFA0e1756B71869E93d4e4Dca5c7d0eb0AA6',
|
||||
blockNumber: 324231,
|
||||
transactionHash: '0x17be1e0745136b9e2857124542f8218812db8fe4458236d5ae045c1ceeb79978',
|
||||
returnValues: {
|
||||
recipient: '0xA84944735B66e957Fe385567dcc85975022Fe68A',
|
||||
value: '100000000000000000000'
|
||||
},
|
||||
event: 'UserRequestForAffirmation'
|
||||
}
|
||||
|
||||
// When
|
||||
const result = normalizeEventInformation(event)
|
||||
|
||||
// Then
|
||||
expect(result.transactionHash).to.equal(event.transactionHash)
|
||||
expect(result.blockNumber).to.equal(event.blockNumber)
|
||||
expect(result.referenceTx).to.equal(event.transactionHash)
|
||||
expect(result.recipient).to.equal(event.returnValues.recipient)
|
||||
expect(result.value).to.equal(event.returnValues.value)
|
||||
})
|
||||
it('should return normalized object for transfer event', () => {
|
||||
// Given
|
||||
const event = {
|
||||
address: '0x89d24A6b4CcB1B6fAA2625fE562bDD9a23260359',
|
||||
blockNumber: 6593953,
|
||||
transactionHash: '0x05afb402e27946d3600b100020dc23419ffd10cb61d3b241cee7b4a84909b48a',
|
||||
returnValues: {
|
||||
from: '0x13C0a8009A578837fB7A80Aa252F6A3ba4aD6B79',
|
||||
to: '0x4aa42145Aa6Ebf72e164C9bBC74fbD3788045016',
|
||||
value: '4000000000000000000'
|
||||
},
|
||||
event: 'Transfer'
|
||||
}
|
||||
|
||||
// When
|
||||
const result = normalizeEventInformation(event)
|
||||
|
||||
// Then
|
||||
expect(result.transactionHash).to.equal(event.transactionHash)
|
||||
expect(result.blockNumber).to.equal(event.blockNumber)
|
||||
expect(result.referenceTx).to.equal(event.transactionHash)
|
||||
expect(result.recipient).to.equal(event.returnValues.from)
|
||||
expect(result.value).to.equal(event.returnValues.value)
|
||||
})
|
||||
it('should return normalized object for RelayedMessage event', () => {
|
||||
// Given
|
||||
const event = {
|
||||
address: '0x4aa42145Aa6Ebf72e164C9bBC74fbD3788045016',
|
||||
blockNumber: 7025826,
|
||||
transactionHash: '0x6ee5969973da763d6d9f162d2dd1b1ec34c2dd977dc39e6b25030b4f04471567',
|
||||
returnValues: {
|
||||
recipient: '0x38BC00Ea43EbB5ef5150593A0BA6C381803717e2',
|
||||
value: '4900000000000000000',
|
||||
transactionHash: '0x5c5c2ab5e333bda4acd035a6a30ea29c7370351891d85373b2d06c7cc6cbb210'
|
||||
},
|
||||
event: 'RelayedMessage'
|
||||
}
|
||||
|
||||
// When
|
||||
const result = normalizeEventInformation(event)
|
||||
|
||||
// Then
|
||||
expect(result.transactionHash).to.equal(event.transactionHash)
|
||||
expect(result.blockNumber).to.equal(event.blockNumber)
|
||||
expect(result.referenceTx).to.equal(event.returnValues.transactionHash)
|
||||
expect(result.recipient).to.equal(event.returnValues.recipient)
|
||||
expect(result.value).to.equal(event.returnValues.value)
|
||||
})
|
||||
it('should return normalized object for AffirmationCompleted event', () => {
|
||||
// Given
|
||||
const event = {
|
||||
address: '0x7301CFA0e1756B71869E93d4e4Dca5c7d0eb0AA6',
|
||||
blockNumber: 474439,
|
||||
transactionHash: '0x654004b372ba32754cef34f403153bbdf43f0fbb3191d5e4683bba7f32e0dc4a',
|
||||
|
||||
returnValues: {
|
||||
recipient: '0x9b7b2B4f7a391b6F14A81221AE0920A9735B67Fb',
|
||||
value: '5000000000000000000',
|
||||
transactionHash: '0xe96da94bbda2cfc865acd3f98040f5c79a627ee9de839d86885d34acd8ecd10d'
|
||||
},
|
||||
event: 'AffirmationCompleted'
|
||||
}
|
||||
|
||||
// When
|
||||
const result = normalizeEventInformation(event)
|
||||
|
||||
// Then
|
||||
expect(result.transactionHash).to.equal(event.transactionHash)
|
||||
expect(result.blockNumber).to.equal(event.blockNumber)
|
||||
expect(result.referenceTx).to.equal(event.returnValues.transactionHash)
|
||||
expect(result.recipient).to.equal(event.returnValues.recipient)
|
||||
expect(result.value).to.equal(event.returnValues.value)
|
||||
})
|
||||
})
|
||||
describe('eventWithoutReference', () => {
|
||||
it('should return false if event is present', () => {
|
||||
// Given
|
||||
const event = {
|
||||
txHash: '0x17be1e0745136b9e2857124542f8218812db8fe4458236d5ae045c1ceeb79978',
|
||||
blockNumber: 474439,
|
||||
referenceTx: '0x17be1e0745136b9e2857124542f8218812db8fe4458236d5ae045c1ceeb79978',
|
||||
recipient: '0x9b7b2B4f7a391b6F14A81221AE0920A9735B67Fb',
|
||||
value: '5000000000000000000'
|
||||
}
|
||||
|
||||
const otherSideEvents = [
|
||||
{
|
||||
txHash: '0x17be1e0745136b9e2857124542f8218812db8fe4458236d5ae045c1ceeb79978',
|
||||
blockNumber: 474439,
|
||||
referenceTx: '0x17be1e0745136b9e2857124542f8218812db8fe4458236d5ae045c1ceeb79978',
|
||||
recipient: '0x9b7b2B4f7a391b6F14A81221AE0920A9735B67Fb',
|
||||
value: '5000000000000000000'
|
||||
},
|
||||
{
|
||||
txHash: '0x17be1e0745136b9e2857124542f8218812db8fe4458236d5ae045c1ceeb79978',
|
||||
blockNumber: 474439,
|
||||
referenceTx: '0x17be1e0745136b9e2857124542f8218812db8fe4458236d5ae045c1ceeb79978',
|
||||
recipient: '0x38BC00Ea43EbB5ef5150593A0BA6C381803717e2',
|
||||
value: '6000000000000000000'
|
||||
},
|
||||
{
|
||||
txHash: '0x17be1e0745136b9e2857124542f8218812db8fe4458236d5ae045c1ceeb79978',
|
||||
blockNumber: 474439,
|
||||
referenceTx: '0x17be1e0745136b9e2857124542f8218812db8fe4458236d5ae045c1ceeb79978',
|
||||
recipient: '0x38BC00Ea43EbB5ef5150593A0BA6C381803717e2',
|
||||
value: '8000000000000000000'
|
||||
}
|
||||
]
|
||||
|
||||
// When
|
||||
const result = eventWithoutReference(otherSideEvents)(event)
|
||||
|
||||
// Then
|
||||
expect(result).to.equal(false)
|
||||
})
|
||||
it('should return true if event is not present', () => {
|
||||
// Given
|
||||
const event = {
|
||||
txHash: '0xe96da94bbda2cfc865acd3f98040f5c79a627ee9de839d86885d34acd8ecd10d',
|
||||
blockNumber: 474439,
|
||||
referenceTx: '0xe96da94bbda2cfc865acd3f98040f5c79a627ee9de839d86885d34acd8ecd10d',
|
||||
recipient: '0x9b7b2B4f7a391b6F14A81221AE0920A9735B67Fb',
|
||||
value: '2000000000000000000'
|
||||
}
|
||||
|
||||
const otherSideEvents = [
|
||||
{
|
||||
txHash: '0x17be1e0745136b9e2857124542f8218812db8fe4458236d5ae045c1ceeb79978',
|
||||
blockNumber: 474439,
|
||||
referenceTx: '0x17be1e0745136b9e2857124542f8218812db8fe4458236d5ae045c1ceeb79978',
|
||||
recipient: '0x9b7b2B4f7a391b6F14A81221AE0920A9735B67Fb',
|
||||
value: '5000000000000000000'
|
||||
},
|
||||
{
|
||||
txHash: '0x05afb402e27946d3600b100020dc23419ffd10cb61d3b241cee7b4a84909b48a',
|
||||
blockNumber: 474439,
|
||||
referenceTx: '0x05afb402e27946d3600b100020dc23419ffd10cb61d3b241cee7b4a84909b48a',
|
||||
recipient: '0x38BC00Ea43EbB5ef5150593A0BA6C381803717e2',
|
||||
value: '6000000000000000000'
|
||||
},
|
||||
{
|
||||
txHash: '0x17be1e0745136b9e2857124542f8218812db8fe4458236d5ae045c1ceeb79978',
|
||||
blockNumber: 474439,
|
||||
referenceTx: '0x17be1e0745136b9e2857124542f8218812db8fe4458236d5ae045c1ceeb79978',
|
||||
recipient: '0x38BC00Ea43EbB5ef5150593A0BA6C381803717e2',
|
||||
value: '8000000000000000000'
|
||||
}
|
||||
]
|
||||
|
||||
// When
|
||||
const result = eventWithoutReference(otherSideEvents)(event)
|
||||
|
||||
// Then
|
||||
expect(result).to.equal(true)
|
||||
})
|
||||
})
|
@ -11,8 +11,10 @@ const {
|
||||
ERC20_ABI,
|
||||
ERC677_BRIDGE_TOKEN_ABI,
|
||||
getTokenType,
|
||||
getPastEvents
|
||||
getPastEvents,
|
||||
ZERO_ADDRESS
|
||||
} = require('../../commons')
|
||||
const { normalizeEventInformation } = require('./message')
|
||||
|
||||
const {
|
||||
COMMON_HOME_RPC_URL,
|
||||
@ -40,6 +42,7 @@ async function main(mode) {
|
||||
const v1Bridge = bridgeMode === BRIDGE_MODES.NATIVE_TO_ERC_V1
|
||||
let isExternalErc20
|
||||
let erc20Contract
|
||||
let normalizeEvent = normalizeEventInformation
|
||||
if (bridgeMode !== BRIDGE_MODES.ARBITRARY_MESSAGE) {
|
||||
const erc20MethodName = bridgeMode === BRIDGE_MODES.NATIVE_TO_ERC || v1Bridge ? 'erc677token' : 'erc20token'
|
||||
const erc20Address = await foreignBridge.methods[erc20MethodName]().call()
|
||||
@ -49,47 +52,98 @@ async function main(mode) {
|
||||
)
|
||||
isExternalErc20 = tokenType === ERC_TYPES.ERC20
|
||||
erc20Contract = new web3Foreign.eth.Contract(ERC20_ABI, erc20Address)
|
||||
} else {
|
||||
normalizeEvent = e => e
|
||||
}
|
||||
|
||||
logger.debug('getting last block numbers')
|
||||
const [homeBlockNumber, foreignBlockNumber] = await getBlockNumber(web3Home, web3Foreign)
|
||||
|
||||
logger.debug("calling homeBridge.getPastEvents('UserRequestForSignature')")
|
||||
const homeToForeignRequests = await getPastEvents(homeBridge, {
|
||||
const homeToForeignRequests = (await getPastEvents(homeBridge, {
|
||||
event: v1Bridge ? 'Deposit' : 'UserRequestForSignature',
|
||||
fromBlock: MONITOR_HOME_START_BLOCK,
|
||||
toBlock: homeBlockNumber
|
||||
})
|
||||
})).map(normalizeEvent)
|
||||
|
||||
logger.debug("calling foreignBridge.getPastEvents('RelayedMessage')")
|
||||
const homeToForeignConfirmations = await getPastEvents(foreignBridge, {
|
||||
const homeToForeignConfirmations = (await getPastEvents(foreignBridge, {
|
||||
event: v1Bridge ? 'Deposit' : 'RelayedMessage',
|
||||
fromBlock: MONITOR_FOREIGN_START_BLOCK,
|
||||
toBlock: foreignBlockNumber
|
||||
})
|
||||
})).map(normalizeEvent)
|
||||
|
||||
logger.debug("calling homeBridge.getPastEvents('AffirmationCompleted')")
|
||||
const foreignToHomeConfirmations = await getPastEvents(homeBridge, {
|
||||
const foreignToHomeConfirmations = (await getPastEvents(homeBridge, {
|
||||
event: v1Bridge ? 'Withdraw' : 'AffirmationCompleted',
|
||||
fromBlock: MONITOR_HOME_START_BLOCK,
|
||||
toBlock: homeBlockNumber
|
||||
})
|
||||
})).map(normalizeEvent)
|
||||
|
||||
logger.debug("calling foreignBridge.getPastEvents('UserRequestForAffirmation')")
|
||||
const foreignToHomeRequests = isExternalErc20
|
||||
? await getPastEvents(erc20Contract, {
|
||||
event: 'Transfer',
|
||||
fromBlock: MONITOR_FOREIGN_START_BLOCK,
|
||||
toBlock: foreignBlockNumber,
|
||||
options: {
|
||||
filter: { to: COMMON_FOREIGN_BRIDGE_ADDRESS }
|
||||
}
|
||||
})
|
||||
: await getPastEvents(foreignBridge, {
|
||||
event: v1Bridge ? 'Withdraw' : 'UserRequestForAffirmation',
|
||||
let foreignToHomeRequests = (await getPastEvents(foreignBridge, {
|
||||
event: v1Bridge ? 'Withdraw' : 'UserRequestForAffirmation',
|
||||
fromBlock: MONITOR_FOREIGN_START_BLOCK,
|
||||
toBlock: foreignBlockNumber
|
||||
})).map(normalizeEvent)
|
||||
if (isExternalErc20) {
|
||||
logger.debug("calling erc20Contract.getPastEvents('Transfer')")
|
||||
let transferEvents = (await getPastEvents(erc20Contract, {
|
||||
event: 'Transfer',
|
||||
fromBlock: MONITOR_FOREIGN_START_BLOCK,
|
||||
toBlock: foreignBlockNumber,
|
||||
options: {
|
||||
filter: { to: COMMON_FOREIGN_BRIDGE_ADDRESS }
|
||||
}
|
||||
})).map(normalizeEvent)
|
||||
|
||||
let directTransfers = transferEvents
|
||||
const tokensSwappedAbiExists = FOREIGN_ABI.filter(e => e.type === 'event' && e.name === 'TokensSwapped')[0]
|
||||
if (tokensSwappedAbiExists) {
|
||||
const tokensSwappedEvents = await getPastEvents(foreignBridge, {
|
||||
event: 'TokensSwapped',
|
||||
fromBlock: MONITOR_FOREIGN_START_BLOCK,
|
||||
toBlock: foreignBlockNumber
|
||||
})
|
||||
|
||||
// Get token swap events emitted by foreign bridge
|
||||
const bridgeTokensSwappedEvents = tokensSwappedEvents.filter(e => e.address === COMMON_FOREIGN_BRIDGE_ADDRESS)
|
||||
|
||||
// Get transfer events for each previous erc20
|
||||
const uniqueTokenAddresses = [...new Set(bridgeTokensSwappedEvents.map(e => e.returnValues.from))]
|
||||
await Promise.all(
|
||||
uniqueTokenAddresses.map(async tokenAddress => {
|
||||
const previousERC20 = new web3Foreign.eth.Contract(ERC20_ABI, tokenAddress)
|
||||
|
||||
const previousTransferEvents = (await getPastEvents(previousERC20, {
|
||||
event: 'Transfer',
|
||||
fromBlock: MONITOR_FOREIGN_START_BLOCK,
|
||||
toBlock: foreignBlockNumber,
|
||||
options: {
|
||||
filter: { to: COMMON_FOREIGN_BRIDGE_ADDRESS }
|
||||
}
|
||||
})).map(normalizeEvent)
|
||||
transferEvents = [...previousTransferEvents, ...transferEvents]
|
||||
})
|
||||
)
|
||||
|
||||
// filter transfer that is part of a token swap
|
||||
directTransfers = transferEvents.filter(
|
||||
e =>
|
||||
bridgeTokensSwappedEvents.findIndex(
|
||||
t => t.transactionHash === e.referenceTx && e.recipient === ZERO_ADDRESS
|
||||
) === -1
|
||||
)
|
||||
}
|
||||
|
||||
// Get transfer events that didn't have a UserRequestForAffirmation event in the same transaction
|
||||
directTransfers = directTransfers.filter(
|
||||
e => foreignToHomeRequests.findIndex(t => t.referenceTx === e.referenceTx) === -1
|
||||
)
|
||||
|
||||
foreignToHomeRequests = [...foreignToHomeRequests, ...directTransfers]
|
||||
}
|
||||
|
||||
logger.debug('Done')
|
||||
return {
|
||||
homeToForeignRequests,
|
||||
|
@ -41,7 +41,32 @@ function messageEqualsEvent(parsedMsg, event) {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes the different event objects to facilitate data processing
|
||||
* @param {Object} event
|
||||
* @returns {{
|
||||
* transactionHash: string,
|
||||
* blockNumber: number,
|
||||
* referenceTx: string,
|
||||
* recipient: string | *,
|
||||
* value: *
|
||||
* }}
|
||||
*/
|
||||
const normalizeEventInformation = event => ({
|
||||
transactionHash: event.transactionHash,
|
||||
blockNumber: event.blockNumber,
|
||||
referenceTx: event.returnValues.transactionHash || event.transactionHash,
|
||||
recipient: event.returnValues.recipient || event.returnValues.from,
|
||||
value: event.returnValues.value
|
||||
})
|
||||
|
||||
const eventWithoutReference = otherSideEvents => e =>
|
||||
otherSideEvents.filter(a => a.referenceTx === e.referenceTx && a.recipient === e.recipient && a.value === e.value)
|
||||
.length === 0
|
||||
|
||||
module.exports = {
|
||||
deliveredMsgNotProcessed,
|
||||
processedMsgNotDelivered
|
||||
processedMsgNotDelivered,
|
||||
normalizeEventInformation,
|
||||
eventWithoutReference
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
const Web3 = require('web3')
|
||||
const assert = require('assert')
|
||||
const promiseRetry = require('promise-retry')
|
||||
const { user, ercToErcBridge, homeRPC, foreignRPC } = require('../../e2e-commons/constants.json')
|
||||
const { ERC677_BRIDGE_TOKEN_ABI } = require('../../commons')
|
||||
const { user, secondUser, ercToErcBridge, homeRPC, foreignRPC } = require('../../e2e-commons/constants.json')
|
||||
const { ERC677_BRIDGE_TOKEN_ABI, FOREIGN_ERC_TO_NATIVE_ABI } = require('../../commons')
|
||||
const { generateNewBlock } = require('../../e2e-commons/utils')
|
||||
|
||||
const homeWeb3 = new Web3(new Web3.providers.HttpProvider(homeRPC.URL))
|
||||
@ -17,6 +17,7 @@ homeWeb3.eth.accounts.wallet.add(user.privateKey)
|
||||
foreignWeb3.eth.accounts.wallet.add(user.privateKey)
|
||||
|
||||
const erc20Token = new foreignWeb3.eth.Contract(ERC677_BRIDGE_TOKEN_ABI, ercToErcBridge.foreignToken)
|
||||
const foreignBridge = new foreignWeb3.eth.Contract(FOREIGN_ERC_TO_NATIVE_ABI, COMMON_FOREIGN_BRIDGE_ADDRESS)
|
||||
const erc677Token = new homeWeb3.eth.Contract(ERC677_BRIDGE_TOKEN_ABI, ercToErcBridge.homeToken)
|
||||
|
||||
describe('erc to erc', () => {
|
||||
@ -24,9 +25,49 @@ describe('erc to erc', () => {
|
||||
const balance = await erc20Token.methods.balanceOf(user.address).call()
|
||||
assert(!toBN(balance).isZero(), 'Account should have tokens')
|
||||
|
||||
const firstTransferValue = homeWeb3.utils.toWei('0.01')
|
||||
|
||||
// approve tokens to foreign bridge
|
||||
await erc20Token.methods
|
||||
.approve(COMMON_FOREIGN_BRIDGE_ADDRESS, firstTransferValue)
|
||||
.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(secondUser.address, firstTransferValue)
|
||||
.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 balance = await erc677Token.methods.balanceOf(user.address).call()
|
||||
const recipientBalance = await erc677Token.methods.balanceOf(secondUser.address).call()
|
||||
assert(toBN(balance).isZero(), 'User balance should be the same')
|
||||
if (toBN(recipientBalance).isZero()) {
|
||||
retry()
|
||||
}
|
||||
})
|
||||
|
||||
const secondTransferValue = homeWeb3.utils.toWei('0.05')
|
||||
|
||||
// send tokens to foreign bridge
|
||||
await erc20Token.methods
|
||||
.transfer(COMMON_FOREIGN_BRIDGE_ADDRESS, homeWeb3.utils.toWei('0.01'))
|
||||
.transfer(COMMON_FOREIGN_BRIDGE_ADDRESS, secondTransferValue)
|
||||
.send({
|
||||
from: user.address,
|
||||
gas: '1000000'
|
||||
@ -44,6 +85,8 @@ describe('erc to erc', () => {
|
||||
const balance = await erc677Token.methods.balanceOf(user.address).call()
|
||||
if (toBN(balance).isZero()) {
|
||||
retry()
|
||||
} else {
|
||||
assert(toBN(balance).eq(toBN(secondTransferValue)), 'User balance should be increased only by second transfer')
|
||||
}
|
||||
})
|
||||
})
|
||||
|
@ -1,8 +1,8 @@
|
||||
const Web3 = require('web3')
|
||||
const assert = require('assert')
|
||||
const promiseRetry = require('promise-retry')
|
||||
const { user, ercToNativeBridge, homeRPC, foreignRPC } = require('../../e2e-commons/constants.json')
|
||||
const { ERC677_BRIDGE_TOKEN_ABI } = require('../../commons')
|
||||
const { user, secondUser, ercToNativeBridge, homeRPC, foreignRPC } = require('../../e2e-commons/constants.json')
|
||||
const { ERC677_BRIDGE_TOKEN_ABI, FOREIGN_ERC_TO_NATIVE_ABI } = require('../../commons')
|
||||
const { generateNewBlock } = require('../../e2e-commons/utils')
|
||||
|
||||
const homeWeb3 = new Web3(new Web3.providers.HttpProvider(homeRPC.URL))
|
||||
@ -17,16 +17,56 @@ homeWeb3.eth.accounts.wallet.add(user.privateKey)
|
||||
foreignWeb3.eth.accounts.wallet.add(user.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', () => {
|
||||
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)
|
||||
const initialBalanceSecondUser = await homeWeb3.eth.getBalance(secondUser.address)
|
||||
assert(!toBN(balance).isZero(), 'Account should have tokens')
|
||||
|
||||
// approve tokens to foreign bridge
|
||||
await erc20Token.methods
|
||||
.approve(COMMON_FOREIGN_BRIDGE_ADDRESS, homeWeb3.utils.toWei('0.01'))
|
||||
.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(secondUser.address, homeWeb3.utils.toWei('0.01'))
|
||||
.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 balance = await homeWeb3.eth.getBalance(user.address)
|
||||
const secondUserbalance = await homeWeb3.eth.getBalance(secondUser.address)
|
||||
assert(toBN(balance).lte(toBN(originalBalanceOnHome)), 'User balance should be the same')
|
||||
if (toBN(secondUserbalance).lte(toBN(initialBalanceSecondUser))) {
|
||||
retry()
|
||||
}
|
||||
})
|
||||
|
||||
const transferValue = homeWeb3.utils.toWei('0.05')
|
||||
|
||||
// send tokens to foreign bridge
|
||||
await erc20Token.methods
|
||||
.transfer(COMMON_FOREIGN_BRIDGE_ADDRESS, homeWeb3.utils.toWei('0.01'))
|
||||
.transfer(COMMON_FOREIGN_BRIDGE_ADDRESS, transferValue)
|
||||
.send({
|
||||
from: user.address,
|
||||
gas: '1000000'
|
||||
@ -44,6 +84,11 @@ describe('erc to native', () => {
|
||||
const balance = await homeWeb3.eth.getBalance(user.address)
|
||||
if (toBN(balance).lte(toBN(originalBalanceOnHome))) {
|
||||
retry()
|
||||
} else {
|
||||
assert(
|
||||
toBN(balance).eq(toBN(originalBalanceOnHome).add(toBN(transferValue))),
|
||||
'User balance should be increased only by second transfer'
|
||||
)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
@ -53,7 +53,7 @@ For more information on the Redis/RabbitMQ requirements, see [#90](/../../issues
|
||||
|
||||
**Note:** The following steps detail the bridge deployment process for development and testing. For deployment in a production environment we recommend using the [Bridge Deployment Playbooks](../deployment/README.md).
|
||||
|
||||
1. [Deploy the bridge contracts](https://github.com/poanetwork/poa-bridge-contracts/blob/master/deploy/README.md)
|
||||
1. [Deploy the bridge contracts](https://github.com/poanetwork/tokenbridge-contracts/blob/master/deploy/README.md)
|
||||
|
||||
2. Open `bridgeDeploymentResults.json` or copy the JSON output generated by the bridge contract deployment process.
|
||||
|
||||
@ -232,10 +232,10 @@ When running the processes, the following commands can be used to test functiona
|
||||
| `USER_ADDRESS` | An account - the current owner of coins/tokens. |
|
||||
| `USER_ADDRESS_PRIVATE_KEY` | A private key belonging to the account. |
|
||||
| `COMMON_HOME_BRIDGE_ADDRESS` | Address of the bridge in the Home network to send transactions. |
|
||||
| `HOME_MIN_AMOUNT_PER_TX` | Value (in _eth_ or tokens) to be sent in one transaction for the Home network. This should be greater than or equal to the value specified in the `poa-bridge-contracts/deploy/.env` file. The default value in that file is 500000000000000000, which is equivalent to 0.5. |
|
||||
| `HOME_MIN_AMOUNT_PER_TX` | Value (in _eth_ or tokens) to be sent in one transaction for the Home network. This should be greater than or equal to the value specified in the `tokenbridge-contracts/deploy/.env` file. The default value in that file is 500000000000000000, which is equivalent to 0.5. |
|
||||
| `HOME_TEST_TX_GAS_PRICE` | The gas price (in Wei) that is used to send transactions in the Home network . |
|
||||
| `COMMON_FOREIGN_BRIDGE_ADDRESS` | Address of the bridge in the Foreign network to send transactions. |
|
||||
| `FOREIGN_MIN_AMOUNT_PER_TX` | Value (in _eth_ or tokens) to be sent in one transaction for the Foreign network. This should be greater than or equal to the value specified in the `poa-bridge-contracts/deploy/.env` file. The default value in that file is 500000000000000000, which is equivalent to 0.5. |
|
||||
| `FOREIGN_MIN_AMOUNT_PER_TX` | Value (in _eth_ or tokens) to be sent in one transaction for the Foreign network. This should be greater than or equal to the value specified in the `tokenbridge-contracts/deploy/.env` file. The default value in that file is 500000000000000000, which is equivalent to 0.5. |
|
||||
| `FOREIGN_TEST_TX_GAS_PRICE` | The gas price (in Wei) that is used to send transactions in the Foreign network . |
|
||||
|
||||
## Contributing
|
||||
|
@ -1,5 +1,5 @@
|
||||
const baseConfig = require('./base.config')
|
||||
const { ERC20_ABI, ERC_TYPES } = require('../../commons')
|
||||
const { ERC_TYPES } = require('../../commons')
|
||||
|
||||
const initialChecksJson = process.argv[3]
|
||||
|
||||
@ -20,24 +20,11 @@ if (baseConfig.id === 'erc-erc' && initialChecks.foreignERC === ERC_TYPES.ERC677
|
||||
|
||||
const id = `${baseConfig.id}-affirmation-request`
|
||||
|
||||
module.exports =
|
||||
(baseConfig.id === 'erc-erc' && initialChecks.foreignERC === ERC_TYPES.ERC20) || baseConfig.id === 'erc-native'
|
||||
? {
|
||||
...baseConfig.bridgeConfig,
|
||||
...baseConfig.foreignConfig,
|
||||
event: 'Transfer',
|
||||
eventContractAddress: initialChecks.bridgeableTokenAddress,
|
||||
eventAbi: ERC20_ABI,
|
||||
eventFilter: { to: process.env.COMMON_FOREIGN_BRIDGE_ADDRESS },
|
||||
queue: 'home',
|
||||
name: `watcher-${id}`,
|
||||
id
|
||||
}
|
||||
: {
|
||||
...baseConfig.bridgeConfig,
|
||||
...baseConfig.foreignConfig,
|
||||
event: 'UserRequestForAffirmation',
|
||||
queue: 'home',
|
||||
name: `watcher-${id}`,
|
||||
id
|
||||
}
|
||||
module.exports = {
|
||||
...baseConfig.bridgeConfig,
|
||||
...baseConfig.foreignConfig,
|
||||
event: 'UserRequestForAffirmation',
|
||||
queue: 'home',
|
||||
name: `watcher-${id}`,
|
||||
id
|
||||
}
|
||||
|
42
oracle/config/transfer-watcher.config.js
Normal file
42
oracle/config/transfer-watcher.config.js
Normal file
@ -0,0 +1,42 @@
|
||||
const baseConfig = require('./base.config')
|
||||
const { ERC20_ABI, ERC_TYPES } = 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.')
|
||||
}
|
||||
|
||||
if (baseConfig.id === 'erc-erc' && initialChecks.foreignERC === ERC_TYPES.ERC677) {
|
||||
baseConfig.id = 'erc677-erc677'
|
||||
}
|
||||
|
||||
const id = `${baseConfig.id}-transfer`
|
||||
|
||||
const transferWatcherRequired =
|
||||
(baseConfig.id === 'erc-erc' && initialChecks.foreignERC === ERC_TYPES.ERC20) || 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.bridgeableTokenAddress,
|
||||
eventAbi: ERC20_ABI,
|
||||
eventFilter: { to: process.env.COMMON_FOREIGN_BRIDGE_ADDRESS },
|
||||
queue: 'home',
|
||||
name: `watcher-${id}`,
|
||||
id
|
||||
}
|
91
oracle/docker-compose-transfer.yml
Normal file
91
oracle/docker-compose-transfer.yml
Normal file
@ -0,0 +1,91 @@
|
||||
---
|
||||
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_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_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_senderhome:
|
||||
driver: bridge
|
||||
net_rabbit_bridge_senderforeign:
|
||||
driver: bridge
|
@ -8,9 +8,10 @@
|
||||
"watcher:signature-request": "./scripts/start-worker.sh watcher signature-request-watcher",
|
||||
"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",
|
||||
"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,sender:home,sender:foreign' -c 'red,green,yellow,blue,magenta' 'yarn watcher:signature-request' 'yarn watcher:collected-signatures' 'yarn watcher:affirmation-request' 'yarn sender:home' 'yarn sender:foreign'",
|
||||
"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'",
|
||||
"test": "NODE_ENV=test mocha",
|
||||
"test:watch": "NODE_ENV=test mocha --watch --reporter=min",
|
||||
"coverage": "NODE_ENV=test nyc --reporter=text --reporter=html mocha",
|
||||
|
@ -1,9 +1,9 @@
|
||||
require('../../../env')
|
||||
const promiseLimit = require('promise-limit')
|
||||
const { HttpListProviderError } = require('http-list-provider')
|
||||
const { BRIDGE_VALIDATORS_ABI } = require('../../../../commons')
|
||||
const { BRIDGE_VALIDATORS_ABI, ZERO_ADDRESS } = require('../../../../commons')
|
||||
const rootLogger = require('../../services/logger')
|
||||
const { web3Home } = require('../../services/web3')
|
||||
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')
|
||||
@ -14,6 +14,12 @@ let validatorContract = null
|
||||
|
||||
function processTransfersBuilder(config) {
|
||||
const homeBridge = new web3Home.eth.Contract(config.homeBridgeAbi, config.homeBridgeAddress)
|
||||
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) {
|
||||
const txToSend = []
|
||||
@ -37,6 +43,32 @@ function processTransfersBuilder(config) {
|
||||
|
||||
logger.info({ from, value }, `Processing transfer ${transfer.transactionHash}`)
|
||||
|
||||
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')
|
||||
|
@ -11,6 +11,7 @@ module.exports = {
|
||||
DEFAULT_GAS_PRICE_FACTOR: 1,
|
||||
EXIT_CODES: {
|
||||
GENERAL_ERROR: 1,
|
||||
WATCHER_NOT_REQUIRED: 0,
|
||||
INCOMPATIBILITY: 10,
|
||||
MAX_TIME_REACHED: 11
|
||||
},
|
||||
|
@ -96,9 +96,11 @@ function processEvents(events) {
|
||||
return processCollectedSignatures(events)
|
||||
case 'native-erc-affirmation-request':
|
||||
case 'erc677-erc677-affirmation-request':
|
||||
return processAffirmationRequests(events)
|
||||
case 'erc-erc-affirmation-request':
|
||||
case 'erc-native-affirmation-request':
|
||||
case 'erc-erc-affirmation-request':
|
||||
return processAffirmationRequests(events)
|
||||
case 'erc-erc-transfer':
|
||||
case 'erc-native-transfer':
|
||||
return processTransfers(events)
|
||||
case 'amb-signature-request':
|
||||
return processAMBSignatureRequests(events)
|
||||
|
@ -36,7 +36,7 @@
|
||||
"initialize": "yarn clean && git submodule update --init && yarn install --unsafe-perm --frozen-lockfile && yarn install:deploy && yarn compile:contracts",
|
||||
"build": "yarn workspace ui run build",
|
||||
"lint": "yarn wsrun --exclude token-bridge-contracts lint",
|
||||
"test": "yarn wsrun --exclude monitor --exclude oracle-e2e --exclude ui-e2e --exclude monitor-e2e test",
|
||||
"test": "yarn wsrun --exclude oracle-e2e --exclude ui-e2e --exclude monitor-e2e test",
|
||||
"oracle-e2e": "./oracle-e2e/run-tests.sh",
|
||||
"ui-e2e": "./ui-e2e/run-tests.sh",
|
||||
"clean": "rm -rf ./node_modules ./**/node_modules ./**/**/node_modules ./**/build",
|
||||
|
@ -4,9 +4,9 @@
|
||||
"license": "MIT",
|
||||
"private": true,
|
||||
"devDependencies": {
|
||||
"selenium-webdriver": "3.6.0",
|
||||
"chromedriver": "^77.0.0",
|
||||
"mocha": "^5.2.0",
|
||||
"chromedriver": "76.0.0"
|
||||
"selenium-webdriver": "3.6.0"
|
||||
},
|
||||
"scripts": {
|
||||
"lint": "eslint . --ignore-path ../.eslintignore"
|
||||
|
10
ui/README.md
10
ui/README.md
@ -52,7 +52,7 @@ The following is an example setup using the POA Sokol testnet as the Home networ
|
||||
|
||||
### Dependencies
|
||||
|
||||
- [Smart Contracts](https://github.com/poanetwork/poa-bridge-contracts)
|
||||
- [Smart Contracts](https://github.com/poanetwork/tokenbridge-contracts)
|
||||
- [Oracle](../oracle/README.md)
|
||||
- [Node.js](https://nodejs.org/en/download/)
|
||||
- [AlphaWallet](https://alphawallet.com/) or [Nifty Wallet](https://github.com/poanetwork/nifty-wallet) or [MetaMask](https://metamask.io/)
|
||||
@ -72,8 +72,8 @@ The following is an example setup using the POA Sokol testnet as the Home networ
|
||||
* Get free Kovan Coins from the [gitter channel](https://gitter.im/kovan-testnet/faucet) or [Iracus faucet](https://github.com/kovan-testnet/faucet) for Foreign account(s). Get 5 Keth to 1 acc, and transfer from there to all other wallets if more than one account is used.
|
||||
|
||||
4. Deploy the Sokol <-> Kovan Bridge contracts.
|
||||
* Go to the the `sokol-kovan-bridge` folder created in step 1 and `git clone https://github.com/poanetwork/poa-bridge-contracts`
|
||||
* Follow instructions in the [POA Bridge contracts repo](https://github.com/poanetwork/poa-bridge-contracts).
|
||||
* Go to the the `sokol-kovan-bridge` folder created in step 1 and `git clone https://github.com/poanetwork/tokenbridge-contracts`
|
||||
* Follow instructions in the [POA Bridge contracts repo](https://github.com/poanetwork/tokenbridge-contracts).
|
||||
* Set the parameters in the .env file.
|
||||
* `DEPLOYMENT_ACCOUNT_PRIVATE_KEY`: Export the private key from step 2
|
||||
* `HOME_RPC_URL`=https://sokol.poa.network
|
||||
@ -86,7 +86,7 @@ The following is an example setup using the POA Sokol testnet as the Home networ
|
||||
* `FOREIGN_UPGRADEABLE_ADMIN_BRIDGE`
|
||||
* `VALIDATORS` _Note: Wallet address(es) for validator(s) are separated by a space. For testing, you can use the same address that was used as the bridge contracts management account._
|
||||
* `FOREIGN_RPC_URL`=https://kovan.infura.io/mew
|
||||
* When deployment is finished, check that the `bridgeDeploymentResults.json` file exists in the `poa-bridge-contracts/deploy` directory and includes the bridge contract addresses.
|
||||
* When deployment is finished, check that the `bridgeDeploymentResults.json` file exists in the `tokenbridge-contracts/deploy` directory and includes the bridge contract addresses.
|
||||
|
||||
5. Install and run the TokenBridge Oracle.
|
||||
* Go to the `sokol-kovan-bridge` folder
|
||||
@ -125,7 +125,7 @@ cp .env.example .env
|
||||
````
|
||||
* Insert the addresses from the bridgeDeploymentResults.json file (from step 4) into the .env file. No other changes are needed, see [Env Parameter Details](#env-parameter-details) for information about each parameter.
|
||||
```
|
||||
cat ../poa-bridge-contracts/deploy/bridgeDeploymentResults.json
|
||||
cat ../tokenbridge-contracts/deploy/bridgeDeploymentResults.json
|
||||
```
|
||||
|
||||
```bash
|
||||
|
@ -60,7 +60,7 @@ class GasPriceStore {
|
||||
|
||||
@computed
|
||||
get gasPriceInHex() {
|
||||
return toHex(this.gasPrice)
|
||||
return toHex(this.gasPrice.toString())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -64,7 +64,7 @@ class TxStore {
|
||||
try {
|
||||
return this.web3Store.getWeb3Promise.then(async () => {
|
||||
if (this.web3Store.defaultAccount.address) {
|
||||
const data = await contract.methods.transferAndCall(to, value, '0x00').encodeABI()
|
||||
const data = await contract.methods.transferAndCall(to, value, '0x').encodeABI()
|
||||
return this.doSend({ to: tokenAddress, from, value: '0x00', data, sentValue: value })
|
||||
} else {
|
||||
this.alertStore.pushError('Please unlock wallet')
|
||||
|
Loading…
Reference in New Issue
Block a user