Compare commits

..

25 Commits

Author SHA1 Message Date
Alexander Kolotov
322729ae82 remove redis installation and redis usage to check if the oracle is running 2020-06-03 01:37:52 +03:00
Alexander Kolotov
636f053c48 rollback dependencies since docker-compose 2.4 file does not support extension of services with depends_on 2020-06-03 01:37:08 +03:00
Alexander Kolotov
80841a76a6 depends added to all services 2020-06-02 03:14:31 +03:00
Alexander Kolotov
994562a8b9 another way to stop and start 2020-06-02 02:45:20 +03:00
Alexander Kolotov
b86090a5a0 more debug info 2020-06-02 02:13:10 +03:00
Alexander Kolotov
a07cecccc2 output ansible tasks through debug 2020-06-02 01:33:00 +03:00
Alexander Kolotov
8556e7aec5 getting more information about e2e testing 2020-06-02 00:59:05 +03:00
Alexander Kolotov
441224c1f0 try a more robust way to indentify if Redis is running in e2e tests 2020-06-02 00:25:16 +03:00
Alexander Kolotov
db11aa6444 Merge branch 'develop' into oracle-services-dependency 2020-06-01 23:00:53 +03:00
Gerardo Nardelli
4db62d721d Add UI e2e and ultimate tests for stake mediators and fix transfer finalization detection (#347) 2020-06-01 20:58:03 +03:00
Gerardo Nardelli
8d6acd0339 Update UI transfer loading to wait block confirmations from contracts (#346) 2020-06-01 17:27:30 +03:00
Gerardo Nardelli
c013cc7378 Bridge UI stake styles fixes (#343) 2020-06-01 17:16:40 +03:00
Alexander Kolotov
d5e7e06788 added dependency between services to prioritize startup sequence 2020-05-26 22:42:18 +03:00
Gerardo Nardelli
d6e39f34af Prepare for metamask breaking changes (#341) 2020-05-26 22:36:22 +03:00
Alexander Kolotov
3b368ce644 Update the contract's submodule to the release 5.0.0 (#342) 2020-05-26 16:32:59 +03:00
Gerardo Nardelli
c1d58c2908 Add stake theme for Bridge UI (#340) 2020-05-25 23:30:41 +03:00
Kirill Fedoseev
d17e9e0eea Support of multiple AMB requests in one transaction (#315) 2020-05-21 23:29:56 +03:00
Kirill Fedoseev
a2c678d0a2 Add confirmRelay script (#333) 2020-05-21 20:23:00 +03:00
Gerardo Nardelli
4bd3576691 Display latest operations in UI status page for mediators (#332) 2020-05-19 08:53:36 +03:00
Gerardo Nardelli
d3576f5a79 Add network list dropdown in UI (#330) 2020-05-18 23:39:39 +03:00
Gerardo Nardelli
62f9a080c9 Add support for STAKE token ERC677-to-ERC677 through AMB mode in Bridge UI (#328) 2020-05-18 23:36:52 +03:00
Alexander Kolotov
10f67168a7 Health field in the monitor reports (#334) 2020-05-18 21:24:16 +03:00
Kirill Fedoseev
f90f888ae4 Return back filter for tx with TokensSwapped (#335) 2020-05-17 17:57:49 +03:00
Kirill Fedoseev
84508e2b84 Update SAI handling in monitor after ES (#331) 2020-05-16 14:08:08 +03:00
Kirill Fedoseev
2369e876aa Upgrade minimist and kind-of dependencies (#325) 2020-05-08 20:14:06 +03:00
168 changed files with 3729 additions and 838 deletions

View File

@@ -51,28 +51,25 @@ orbs:
paths:
- ~/.cache/yarn
wait-for-oracle:
parameters:
redis-key:
type: string
steps:
- run:
name: Install redis tools
command: sudo apt-get install -y redis-tools
- run:
name: Wait for the Oracle to start
command: |
set +e
i=0
while [[ $(redis-cli GET << parameters.redis-key >> ) ]]; do
((i++))
if [ "$i" -gt 30 ]
then
exit -1
while :
do
(echo > /dev/tcp/127.0.0.1/6379) >/dev/null 2>&1
if [[ $? -eq 0 ]]; then
break
fi
((i++))
if [ "$i" -gt 30 ]; then
echo "Redis has not open the port"
exit 1
fi
echo "Sleeping..."
sleep 3
done
done
executors:
docker-node:
docker:
@@ -225,9 +222,6 @@ jobs:
scenario-name:
description: "Molecule scenario name used to create the infrastructure"
type: string
redis-key:
description: "Redis key checked for non-emptiness to assert if Oracle is running"
type: string
ui-e2e-grep:
description: "Mocha grep string used to run ui-e2e tests specific to given type of bridge"
default: ''
@@ -247,8 +241,7 @@ jobs:
name: Prepare the infrastructure
command: e2e-commons/up.sh deploy << parameters.scenario-name >> blocks
no_output_timeout: 50m
- tokenbridge-orb/wait-for-oracle:
redis-key: << parameters.redis-key >>
- tokenbridge-orb/wait-for-oracle
- when:
condition: << parameters.ui-e2e-grep >>
steps:
@@ -297,20 +290,20 @@ workflows:
- ultimate:
name: "ultimate: native to erc"
scenario-name: native-to-erc
redis-key: native-erc-collected-signatures:lastProcessedBlock
ui-e2e-grep: "NATIVE TO ERC"
- ultimate:
name: "ultimate: erc to native"
scenario-name: erc-to-native
redis-key: erc-native-collected-signatures:lastProcessedBlock
ui-e2e-grep: "ERC TO NATIVE"
- ultimate:
name: "ultimate: erc to erc"
scenario-name: erc-to-erc
redis-key: erc-erc-collected-signatures:lastProcessedBlock
ui-e2e-grep: "ERC TO ERC"
- ultimate:
name: "ultimate: amb"
scenario-name: amb
redis-key: amb-collected-signatures:lastProcessedBlock
oracle-e2e-script: "amb"
- ultimate:
name: "ultimate: amb stake erc to erc"
scenario-name: ultimate-amb-stake-erc-to-erc
ui-e2e-grep: "AMB-STAKE-ERC-TO-ERC"

View File

@@ -9,6 +9,8 @@
**/*.md
contracts/test
contracts/build
oracle/test
oracle/**/*.png
oracle/**/*.jpg
audit

1
.gitignore vendored
View File

@@ -49,4 +49,5 @@ __pycache__
#monitor
monitor/responses/*
monitor/configs/*.env
!monitor/.gitkeep

View File

@@ -43,6 +43,7 @@ ORACLE_VALIDATOR_ADDRESS | The public address of the bridge validator | hexideci
name | description | value
--- | --- | ---
UI_TITLE | The title for the bridge UI page. `%c` will be replaced by the name of the network. | string
UI_OG_TITLE | The meta title for the deployed bridge page. | string
UI_DESCRIPTION | The meta description for the deployed bridge page. | string
UI_NATIVE_TOKEN_DISPLAY_NAME | name of the home native coin | string
UI_HOME_NETWORK_DISPLAY_NAME | name to be displayed for home network | string
@@ -56,7 +57,8 @@ UI_FOREIGN_EXPLORER_ADDRESS_TEMPLATE | template link to address on foreign explo
UI_HOME_GAS_PRICE_UPDATE_INTERVAL | An interval in milliseconds used to get the updated gas price value either from the oracle or from the Home Bridge contract. | integer
UI_FOREIGN_GAS_PRICE_UPDATE_INTERVAL | An interval in milliseconds used to get the updated gas price value either from the oracle or from the Foreign Bridge contract. | integer
UI_PORT | The port for the UI app. | integer
UI_STYLES | The set of styles to render the bridge UI page. Currently only `classic` is implemented | classic
UI_STYLES | The set of styles to render the bridge UI page. | core/classic/stake
UI_PUBLIC_URL | The public url for the deployed bridge page | string
## Monitor configuration

View File

@@ -7,13 +7,17 @@ const FOREIGN_ERC_TO_NATIVE_ABI = require('../contracts/build/contracts/ForeignB
const ERC20_ABI = require('../contracts/build/contracts/ERC20').abi
const ERC677_ABI = require('../contracts/build/contracts/ERC677').abi
const ERC677_BRIDGE_TOKEN_ABI = require('../contracts/build/contracts/ERC677BridgeToken').abi
const BLOCK_REWARD_ABI = require('../contracts/build/contracts/IBlockReward').abi
const BLOCK_REWARD_ABI = require('../contracts/build/contracts/BlockReward').abi
const BRIDGE_VALIDATORS_ABI = require('../contracts/build/contracts/BridgeValidators').abi
const REWARDABLE_VALIDATORS_ABI = require('../contracts/build/contracts/RewardableValidators').abi
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_AMB_ERC_TO_ERC_ABI = require('../contracts/build/contracts/HomeAMBErc677ToErc677').abi
const FOREIGN_AMB_ERC_TO_ERC_ABI = require('../contracts/build/contracts/ForeignAMBErc677ToErc677').abi
const HOME_STAKE_ERC_TO_ERC_ABI = require('../contracts/build/contracts/HomeStakeTokenMediator').abi
const FOREIGN_STAKE_ERC_TO_ERC_ABI = require('../contracts/build/contracts/ForeignStakeTokenMediator').abi
const { HOME_V1_ABI, FOREIGN_V1_ABI } = require('./v1Abis')
const { BRIDGE_MODES } = require('./constants')
@@ -67,6 +71,12 @@ function getBridgeABIs(bridgeMode) {
} else if (bridgeMode === BRIDGE_MODES.ARBITRARY_MESSAGE) {
HOME_ABI = HOME_AMB_ABI
FOREIGN_ABI = FOREIGN_AMB_ABI
} else if (bridgeMode === BRIDGE_MODES.AMB_ERC_TO_ERC) {
HOME_ABI = HOME_AMB_ERC_TO_ERC_ABI
FOREIGN_ABI = FOREIGN_AMB_ERC_TO_ERC_ABI
} else if (bridgeMode === BRIDGE_MODES.STAKE_AMB_ERC_TO_ERC) {
HOME_ABI = HOME_STAKE_ERC_TO_ERC_ABI
FOREIGN_ABI = FOREIGN_STAKE_ERC_TO_ERC_ABI
} else {
throw new Error(`Unrecognized bridge mode: ${bridgeMode}`)
}
@@ -94,5 +104,7 @@ module.exports = {
HOME_AMB_ABI,
FOREIGN_AMB_ABI,
BOX_ABI,
SAI_TOP
SAI_TOP,
HOME_STAKE_ERC_TO_ERC_ABI,
FOREIGN_STAKE_ERC_TO_ERC_ABI
}

View File

@@ -3,7 +3,9 @@ const BRIDGE_MODES = {
ERC_TO_ERC: 'ERC_TO_ERC',
ERC_TO_NATIVE: 'ERC_TO_NATIVE',
NATIVE_TO_ERC_V1: 'NATIVE_TO_ERC_V1',
ARBITRARY_MESSAGE: 'ARBITRARY_MESSAGE'
ARBITRARY_MESSAGE: 'ARBITRARY_MESSAGE',
AMB_ERC_TO_ERC: 'AMB_ERC_TO_ERC',
STAKE_AMB_ERC_TO_ERC: 'STAKE_AMB_ERC_TO_ERC'
}
const ERC_TYPES = {
@@ -14,6 +16,7 @@ const ERC_TYPES = {
const FEE_MANAGER_MODE = {
ONE_DIRECTION: 'ONE_DIRECTION',
BOTH_DIRECTIONS: 'BOTH_DIRECTIONS',
ONE_DIRECTION_STAKE: 'ONE_DIRECTION_STAKE',
UNDEFINED: 'UNDEFINED'
}

View File

@@ -9,14 +9,14 @@ function addTxHashToData({ encodedData, transactionHash }) {
function parseAMBMessage(message) {
message = strip0x(message)
const txHash = `0x${message.slice(0, 64)}`
const messageId = `0x${message.slice(0, 64)}`
const sender = `0x${message.slice(64, 104)}`
const executor = `0x${message.slice(104, 144)}`
return {
sender,
executor,
txHash
messageId
}
}

View File

@@ -3,7 +3,7 @@ const { BRIDGE_MODES, ERC_TYPES } = require('../constants')
describe('constants', () => {
it('should contain correct number of bridge types', () => {
expect(Object.keys(BRIDGE_MODES).length).to.be.equal(5)
expect(Object.keys(BRIDGE_MODES).length).to.be.equal(7)
})
it('should contain correct number of erc types', () => {

View File

@@ -61,4 +61,99 @@ describe('getTokenType', () => {
// Then
expect(type).to.equal(ERC_TYPES.ERC20)
})
it('should return ERC20 if bridgeContract and isBridge are not present', async () => {
// Given
const bridgeAddress = '0xCecBE80Ed3548dE11D7d2D922a36576eA40C4c26'
const contract = {
methods: {
bridgeContract: () => {
return {
call: () => Promise.reject()
}
},
isBridge: () => {
return {
call: () => Promise.reject()
}
}
}
}
// When
const type = await getTokenType(contract, bridgeAddress)
// Then
expect(type).to.equal(ERC_TYPES.ERC20)
})
it('should return ERC677 if isBridge returns true', async () => {
// Given
const bridgeAddress = '0xCecBE80Ed3548dE11D7d2D922a36576eA40C4c26'
const contract = {
methods: {
bridgeContract: () => {
return {
call: () => Promise.reject()
}
},
isBridge: () => {
return {
call: () => Promise.resolve(true)
}
}
}
}
// When
const type = await getTokenType(contract, bridgeAddress)
// Then
expect(type).to.equal(ERC_TYPES.ERC677)
})
it('should return ERC677 if isBridge returns true and bridgeContract not present', async () => {
// Given
const bridgeAddress = '0xCecBE80Ed3548dE11D7d2D922a36576eA40C4c26'
const contract = {
methods: {
isBridge: () => {
return {
call: () => Promise.resolve(true)
}
}
}
}
// When
const type = await getTokenType(contract, bridgeAddress)
// Then
expect(type).to.equal(ERC_TYPES.ERC677)
})
it('should return ERC20 if isBridge returns false', async () => {
// Given
const bridgeAddress = '0xCecBE80Ed3548dE11D7d2D922a36576eA40C4c26'
const contract = {
methods: {
bridgeContract: () => {
return {
call: () => Promise.reject()
}
},
isBridge: () => {
return {
call: () => Promise.resolve(false)
}
}
}
}
// When
const type = await getTokenType(contract, bridgeAddress)
// Then
expect(type).to.equal(ERC_TYPES.ERC20)
})
})

View File

@@ -50,20 +50,20 @@ describe('parseAMBMessage', () => {
it('should parse data type 00', () => {
const msgSender = '0x003667154bb32e42bb9e1e6532f19d187fa0082e'
const msgExecutor = '0xf4bef13f9f4f2b203faf0c3cbbaabe1afe056955'
const msgTxHash = '0xbdceda9d8c94838aca10c687da1411a07b1390e88239c0638cb9cc264219cc10'
const msgId = '0xbdceda9d8c94838aca10c687da1411a07b1390e88239c0638cb9cc264219cc10'
const msgGasLimit = '000000000000000000000000000000000000000000000000000000005b877705'
const msgDataType = '00'
const msgData = '0xb1591967aed668a4b27645ff40c444892d91bf5951b382995d4d4f6ee3a2ce03'
const message = `0x${strip0x(msgTxHash)}${strip0x(msgSender)}${strip0x(
const message = `0x${strip0x(msgId)}${strip0x(msgSender)}${strip0x(
msgExecutor
)}${msgGasLimit}${msgDataType}${strip0x(msgData)}`
// when
const { sender, executor, txHash } = parseAMBMessage(message)
const { sender, executor, messageId } = parseAMBMessage(message)
// then
expect(sender).to.be.equal(msgSender)
expect(executor).to.be.equal(msgExecutor)
expect(txHash).to.be.equal(msgTxHash)
expect(messageId).to.be.equal(msgId)
})
})

View File

@@ -12,6 +12,10 @@ function decodeBridgeMode(bridgeModeHash) {
return BRIDGE_MODES.ERC_TO_NATIVE
case '0x2544fbb9':
return BRIDGE_MODES.ARBITRARY_MESSAGE
case '0x16ea01e9':
return BRIDGE_MODES.STAKE_AMB_ERC_TO_ERC
case '0x76595b56':
return BRIDGE_MODES.AMB_ERC_TO_ERC
default:
throw new Error(`Unrecognized bridge mode hash: '${bridgeModeHash}'`)
}
@@ -46,10 +50,31 @@ const getTokenType = async (bridgeTokenContract, bridgeAddress) => {
return ERC_TYPES.ERC20
}
} catch (e) {
return ERC_TYPES.ERC20
try {
const isBridge = await bridgeTokenContract.methods.isBridge(bridgeAddress).call()
if (isBridge) {
return ERC_TYPES.ERC677
} else {
return ERC_TYPES.ERC20
}
} catch (e) {
return ERC_TYPES.ERC20
}
}
}
const isErcToErcMode = bridgeMode => {
return (
bridgeMode === BRIDGE_MODES.ERC_TO_ERC ||
bridgeMode === BRIDGE_MODES.AMB_ERC_TO_ERC ||
bridgeMode === BRIDGE_MODES.STAKE_AMB_ERC_TO_ERC
)
}
const isMediatorMode = bridgeMode => {
return bridgeMode === BRIDGE_MODES.AMB_ERC_TO_ERC || bridgeMode === BRIDGE_MODES.STAKE_AMB_ERC_TO_ERC
}
const getUnit = bridgeMode => {
let unitHome = null
let unitForeign = null
@@ -62,6 +87,9 @@ const getUnit = bridgeMode => {
} else if (bridgeMode === BRIDGE_MODES.ERC_TO_NATIVE) {
unitHome = 'Native coins'
unitForeign = 'Tokens'
} else if (bridgeMode === BRIDGE_MODES.STAKE_AMB_ERC_TO_ERC) {
unitHome = 'Tokens'
unitForeign = 'Tokens'
} else {
throw new Error(`Unrecognized bridge mode: ${bridgeMode}`)
}
@@ -260,5 +288,7 @@ module.exports = {
normalizeGasPrice,
gasPriceFromSupplier,
gasPriceFromContract,
gasPriceWithinLimits
gasPriceWithinLimits,
isErcToErcMode,
isMediatorMode
}

View File

@@ -0,0 +1,14 @@
# Molecule managed
{% if item.registry is defined %}
FROM {{ item.registry.url }}/{{ item.image }}
{% else %}
FROM {{ item.image }}
{% endif %}
RUN if [ $(command -v apt-get) ]; then apt-get update && apt-get install -y python sudo bash ca-certificates && apt-get clean; \
elif [ $(command -v dnf) ]; then dnf makecache && dnf --assumeyes install python sudo python-devel python*-dnf bash && dnf clean all; \
elif [ $(command -v yum) ]; then yum makecache fast && yum install -y python sudo yum-plugin-ovl bash && sed -i 's/plugins=0/plugins=1/g' /etc/yum.conf && yum clean all; \
elif [ $(command -v zypper) ]; then zypper refresh && zypper install -y python sudo bash python-xml && zypper clean -a; \
elif [ $(command -v apk) ]; then apk update && apk add --no-cache python sudo bash ca-certificates; \
elif [ $(command -v xbps-install) ]; then xbps-install -Syu && xbps-install -y python sudo bash ca-certificates && xbps-remove -O; fi

View File

@@ -0,0 +1,54 @@
---
driver:
name: docker
platforms:
- name: oracle-amb-host
groups:
- ultimate
- amb
children:
- oracle
image: ubuntu:16.04
privileged: true
network_mode: host
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- name: ui-amb-stake-erc-to-erc-host
groups:
- ultimate
- amb-stake-erc-to-erc
children:
- ui
image: ubuntu:16.04
privileged: true
network_mode: host
volumes:
- /var/run/docker.sock:/var/run/docker.sock
provisioner:
name: ansible
playbooks:
prepare: ../prepare.yml
converge: ../ultimate-commons/converge.yml
inventory:
host_vars:
oracle-amb-host:
COMMON_HOME_RPC_URL: "http://parity1:8545"
COMMON_FOREIGN_RPC_URL: "http://parity2:8545"
ORACLE_VALIDATOR_ADDRESS: "0xaaB52d66283F7A1D5978bcFcB55721ACB467384b"
ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY: "8e829f695aed89a154550f30262f1529582cc49dc30eff74a6b491359e0230f9"
ui-amb-stake-erc-to-erc-host:
COMMON_HOME_RPC_URL: "http://localhost:8541"
COMMON_FOREIGN_RPC_URL: "http://localhost:8542"
verifier:
name: testinfra
lint:
name: flake8
scenario:
name: ultimate-amb-stake-erc-to-erc
test_sequence:
- cleanup
- destroy
- syntax
- create
- prepare
- converge

View File

@@ -24,3 +24,10 @@
copy:
content: "{{ docker_compose_parsed | to_yaml }}"
dest: "/home/poadocker/bridge/oracle/{{ file }}.yml"
- name: Get updated docker file
shell: cat "/home/poadocker/bridge/oracle/{{ file }}.yml"
register: catout
- debug: var=catout.stdout_lines

View File

@@ -3,9 +3,30 @@
hosts: oracle
become: true
tasks:
- name: get statuses for docker containers
shell: docker ps -a
register: docker1out
- debug: var=docker1out.stdout_lines
- name: get status for poabridge
shell: /etc/init.d/poabridge status
register: serviceout
- debug: var=serviceout.stdout_lines
- name: stop the service
shell: service poabridge stop
- name: force to stop redis and rabit
shell: docker rm -f oracle_rabbit_1 oracle_redis_1 || true
- name: get statuses for docker containers
shell: docker ps -a
register: docker2out
- debug: var=docker2out.stdout_lines
- name: Build current oracle image
shell: docker build -t oracle:ultimate-testing --file oracle/Dockerfile .
delegate_to: 127.0.0.1
@@ -32,4 +53,14 @@
loop_var: file
- name: start the service
shell: service poabridge start
#shell: service poabridge start
shell: /etc/init.d/poabridge start
register: startout
- debug: var=startout.stdout_lines
- name: get statuses for docker containers
shell: docker ps -a
register: docker3out
- debug: var=docker3out.stdout_lines

View File

@@ -0,0 +1,4 @@
---
COMMON_HOME_BRIDGE_ADDRESS: "0xc26Aa60Ff574f157616D3aEE70e08aAC129E1dFC"
COMMON_FOREIGN_BRIDGE_ADDRESS: "0xc26Aa60Ff574f157616D3aEE70e08aAC129E1dFC"
UI_PORT: 3003

View File

@@ -32,6 +32,7 @@ COMMON_FOREIGN_GAS_PRICE_FACTOR: 1
## UI
UI_TITLE: "TokenBridge UI app - %c"
UI_OG_TITLE: "POA Bridge UI"
UI_DESCRIPTION: "The TokenBridge serves as a method of transferring MakerDAO stable tokens between the Ethereum network to xDai chain in a quick and cost-efficient manner."
UI_PORT: 3001
UI_HOME_EXPLORER_TX_TEMPLATE: https://blockscout.com/poa/dai/tx/%s
@@ -40,6 +41,8 @@ UI_HOME_EXPLORER_ADDRESS_TEMPLATE: https://blockscout.com/poa/dai/address/%s
UI_FOREIGN_EXPLORER_ADDRESS_TEMPLATE: https://blockscout.com/eth/mainnet/address/%s
UI_HOME_GAS_PRICE_UPDATE_INTERVAL: 600000
UI_FOREIGN_GAS_PRICE_UPDATE_INTERVAL: 600000
UI_STYLES: "core"
UI_PUBLIC_URL: "https://dai-bridge.poa.network"
## Monitor
MONITOR_BRIDGE_NAME: "xdai"

View File

@@ -34,6 +34,7 @@ ORACLE_FOREIGN_GAS_PRICE_UPDATE_INTERVAL: 600000
## UI
UI_TITLE: "TokenBridge UI app - %c"
UI_OG_TITLE: "POA Bridge UI"
UI_DESCRIPTION: "The POA cross-chain bridge serves as a method of transferring POA native tokens from the POA Network to the Ethereum network in a quick and cost-efficient manner."
UI_PORT: 3001
UI_HOME_EXPLORER_TX_TEMPLATE: https://blockscout.com/poa/sokol/tx/%s
@@ -42,6 +43,8 @@ UI_HOME_EXPLORER_ADDRESS_TEMPLATE: https://blockscout.com/poa/sokol/address/%s
UI_FOREIGN_EXPLORER_ADDRESS_TEMPLATE: https://blockscout.com/eth/kovan/address/%s
UI_HOME_GAS_PRICE_UPDATE_INTERVAL: 600000
UI_FOREIGN_GAS_PRICE_UPDATE_INTERVAL: 600000
UI_STYLES: "core"
UI_PUBLIC_URL: "http://localhost:3001"
## Monitor
MONITOR_BRIDGE_NAME: "bridge"

View File

@@ -33,6 +33,7 @@ ORACLE_FOREIGN_GAS_PRICE_UPDATE_INTERVAL: 600000
#ui
UI_TITLE: "TokenBridge UI app - %c"
UI_OG_TITLE: "POA Bridge UI"
UI_DESCRIPTION: "The POA cross-chain bridge serves as a method of transferring POA native tokens from the POA Network to the Ethereum network in a quick and cost-efficient manner."
UI_HOME_EXPLORER_TX_TEMPLATE: https://blockscout.com/poa/sokol/tx/%s
UI_FOREIGN_EXPLORER_TX_TEMPLATE: https://blockscout.com/eth/kovan/tx/%s
@@ -40,6 +41,8 @@ UI_HOME_EXPLORER_ADDRESS_TEMPLATE: https://blockscout.com/poa/sokol/address/%s
UI_FOREIGN_EXPLORER_ADDRESS_TEMPLATE: https://blockscout.com/eth/kovan/address/%s
UI_HOME_GAS_PRICE_UPDATE_INTERVAL: 600000
UI_FOREIGN_GAS_PRICE_UPDATE_INTERVAL: 600000
UI_STYLES: "core"
UI_PUBLIC_URL: "http://localhost"
#monitor
MONITOR_BRIDGE_NAME: "bridge"

View File

@@ -33,6 +33,7 @@ COMMON_FOREIGN_GAS_PRICE_FACTOR: 1
## UI
UI_TITLE: "TokenBridge UI app - %c"
UI_OG_TITLE: "POA Bridge UI"
UI_DESCRIPTION: "The TokenBridge serves as a method of transferring native tokens from the Ethereum Classic Network to the Ethereum network in a quick and cost-efficient manner."
UI_PORT: 3001
UI_HOME_EXPLORER_TX_TEMPLATE: https://blockscout.com/etc/mainnet/tx/%s
@@ -41,6 +42,8 @@ UI_HOME_EXPLORER_ADDRESS_TEMPLATE: https://blockscout.com/etc/mainnet/address/%s
UI_FOREIGN_EXPLORER_ADDRESS_TEMPLATE: https://blockscout.com/eth/mainnet/address/%s
UI_HOME_GAS_PRICE_UPDATE_INTERVAL: 600000
UI_FOREIGN_GAS_PRICE_UPDATE_INTERVAL: 600000
UI_STYLES: "classic"
UI_PUBLIC_URL: "https://wetc.app"
## Monitor
MONITOR_BRIDGE_NAME: "wetc"

View File

@@ -34,9 +34,9 @@ COMMON_FOREIGN_GAS_PRICE_FACTOR={{ COMMON_FOREIGN_GAS_PRICE_FACTOR }}
# Default
UI_TITLE={{ UI_TITLE }}
UI_OG_TITLE={{ UI_OG_TITLE }}
UI_DESCRIPTION={{ UI_DESCRIPTION }}
UI_PORT={{ UI_PORT }}
UI_PUBLIC_URL={{ UI_PUBLIC_URL }}
{% if UI_STYLES | default('') != '' %}
UI_STYLES={{ UI_STYLES }}
{% endif %}

View File

@@ -21,3 +21,5 @@ COMMON_FOREIGN_GAS_PRICE_FACTOR=1
ORACLE_HOME_RPC_POLLING_INTERVAL=500
ORACLE_FOREIGN_RPC_POLLING_INTERVAL=500
ORACLE_ALLOW_HTTP_FOR_RPC=yes
ORACLE_HOME_START_BLOCK=1
ORACLE_FOREIGN_START_BLOCK=1

View File

@@ -21,3 +21,5 @@ COMMON_FOREIGN_GAS_PRICE_FACTOR=0.1
ORACLE_HOME_RPC_POLLING_INTERVAL=500
ORACLE_FOREIGN_RPC_POLLING_INTERVAL=500
ORACLE_ALLOW_HTTP_FOR_RPC=yes
ORACLE_HOME_START_BLOCK=1
ORACLE_FOREIGN_START_BLOCK=1

View File

@@ -21,3 +21,5 @@ COMMON_FOREIGN_GAS_PRICE_FACTOR=0.1
ORACLE_HOME_RPC_POLLING_INTERVAL=500
ORACLE_FOREIGN_RPC_POLLING_INTERVAL=500
ORACLE_ALLOW_HTTP_FOR_RPC=yes
ORACLE_HOME_START_BLOCK=1
ORACLE_FOREIGN_START_BLOCK=1

View File

@@ -0,0 +1,23 @@
COMMON_HOME_BRIDGE_ADDRESS=0xc26Aa60Ff574f157616D3aEE70e08aAC129E1dFC
COMMON_FOREIGN_BRIDGE_ADDRESS=0xc26Aa60Ff574f157616D3aEE70e08aAC129E1dFC
COMMON_FOREIGN_RPC_URL=http://localhost:8542
COMMON_HOME_RPC_URL=http://localhost:8541
UI_NATIVE_TOKEN_DISPLAY_NAME=POA
UI_HOME_NETWORK_DISPLAY_NAME=Sokol
UI_FOREIGN_NETWORK_DISPLAY_NAME=Kovan
UI_HOME_EXPLORER_TX_TEMPLATE=https://blockscout.com/poa/sokol/tx//%s
UI_FOREIGN_EXPLORER_TX_TEMPLATE=https://blockscout.com/eth/kovan/tx/%s
UI_HOME_EXPLORER_ADDRESS_TEMPLATE=https://blockscout.com/poa/sokol/address/%s
UI_FOREIGN_EXPLORER_ADDRESS_TEMPLATE=https://blockscout.com/eth/kovan/address/%s
COMMON_HOME_GAS_PRICE_SUPPLIER_URL=https://gasprice.poa.network/
COMMON_HOME_GAS_PRICE_SPEED_TYPE=standard
COMMON_HOME_GAS_PRICE_FALLBACK=5000000000
UI_HOME_GAS_PRICE_UPDATE_INTERVAL=15000
COMMON_HOME_GAS_PRICE_FACTOR=1
COMMON_FOREIGN_GAS_PRICE_SUPPLIER_URL=https://gasprice.poa.network/
COMMON_FOREIGN_GAS_PRICE_SPEED_TYPE=standard
COMMON_FOREIGN_GAS_PRICE_FALLBACK=5000000000
UI_FOREIGN_GAS_PRICE_UPDATE_INTERVAL=15000
COMMON_FOREIGN_GAS_PRICE_FACTOR=1
UI_PORT=3000
UI_STYLES=stake

View File

@@ -20,3 +20,4 @@ COMMON_FOREIGN_GAS_PRICE_FALLBACK=5000000000
UI_FOREIGN_GAS_PRICE_UPDATE_INTERVAL=15000
COMMON_FOREIGN_GAS_PRICE_FACTOR=1
UI_PORT=3000
UI_STYLES=core

View File

@@ -20,3 +20,4 @@ COMMON_FOREIGN_GAS_PRICE_FALLBACK=5000000000
UI_FOREIGN_GAS_PRICE_UPDATE_INTERVAL=15000
COMMON_FOREIGN_GAS_PRICE_FACTOR=1
UI_PORT=3000
UI_STYLES=core

View File

@@ -20,3 +20,4 @@ COMMON_FOREIGN_GAS_PRICE_FALLBACK=5000000000
UI_FOREIGN_GAS_PRICE_UPDATE_INTERVAL=15000
COMMON_FOREIGN_GAS_PRICE_FACTOR=1
UI_PORT=3000
UI_STYLES=core

View File

@@ -63,6 +63,14 @@
"foreignBox": "0x6C4EaAb8756d53Bf599FFe2347FAFF1123D6C8A1",
"monitor": "http://monitor-amb:3013/bridge"
},
"ambStakeErcToErc": {
"home": "0xc26Aa60Ff574f157616D3aEE70e08aAC129E1dFC",
"foreign": "0xc26Aa60Ff574f157616D3aEE70e08aAC129E1dFC",
"homeToken": "0x6f359aC418a5f7538F7755A33C9c7dDf38785524",
"foreignToken": "0x6f359aC418a5f7538F7755A33C9c7dDf38785524",
"blockReward": "0xF9698Eb93702dfdd0e2d802088d4c21822a8A977",
"ui": "http://localhost:3003"
},
"homeRPC": {
"URL": "http://parity1:8545",
"ID": "77"

View File

@@ -0,0 +1,30 @@
BRIDGE_MODE=STAKE_AMB_ERC_TO_ERC
DEPLOYMENT_ACCOUNT_PRIVATE_KEY=8e829f695aed89a154550f30262f1529582cc49dc30eff74a6b491359e0230f9
DEPLOYMENT_GAS_LIMIT_EXTRA=0.2
HOME_DEPLOYMENT_GAS_PRICE=10000000000
FOREIGN_DEPLOYMENT_GAS_PRICE=10000000000
GET_RECEIPT_INTERVAL_IN_MILLISECONDS=50
HOME_STAKE_TOKEN_ADDRESS=0x6f359aC418a5f7538F7755A33C9c7dDf38785524
FOREIGN_STAKE_TOKEN_ADDRESS=0x6f359aC418a5f7538F7755A33C9c7dDf38785524
BLOCK_REWARD_ADDRESS=0xF9698Eb93702dfdd0e2d802088d4c21822a8A977
HOME_RPC_URL=http://parity1:8545
HOME_BRIDGE_OWNER=0xaaB52d66283F7A1D5978bcFcB55721ACB467384b
HOME_UPGRADEABLE_ADMIN=0xaaB52d66283F7A1D5978bcFcB55721ACB467384b
HOME_DAILY_LIMIT=30000000000000000000000000
HOME_MAX_AMOUNT_PER_TX=1500000000000000000000000
HOME_MIN_AMOUNT_PER_TX=10000000000000000
HOME_TRANSACTIONS_FEE=0
FOREIGN_RPC_URL=http://parity2:8545
FOREIGN_BRIDGE_OWNER=0xaaB52d66283F7A1D5978bcFcB55721ACB467384b
FOREIGN_UPGRADEABLE_ADMIN=0xaaB52d66283F7A1D5978bcFcB55721ACB467384b
FOREIGN_DAILY_LIMIT=15000000000000000000000000
FOREIGN_MAX_AMOUNT_PER_TX=750000000000000000000000
FOREIGN_MIN_AMOUNT_PER_TX=10000000000000000
HOME_AMB_BRIDGE=0x0AEe1FCD12dDFab6265F7f8956e6E012A9Fe4Aa0
FOREIGN_AMB_BRIDGE=0x0AEe1FCD12dDFab6265F7f8956e6E012A9Fe4Aa0
HOME_MEDIATOR_REQUEST_GAS_LIMIT=2000000
FOREIGN_MEDIATOR_REQUEST_GAS_LIMIT=2000000
BRIDGEABLE_TOKEN_NAME='not used'
BRIDGEABLE_TOKEN_SYMBOL='not used'
BRIDGEABLE_TOKEN_DECIMALS='18'

View File

@@ -93,6 +93,15 @@ services:
command: "true"
networks:
- ultimate
ui-amb-stake-erc20-erc20:
build:
context: ..
dockerfile: ui/Dockerfile
args:
DOT_ENV_PATH: e2e-commons/components-envs/ui-amb-stake-erc20-erc20.env
command: "true"
networks:
- ultimate
monitor:
build:
context: ..

View File

@@ -43,3 +43,13 @@ echo -e "\n\n############ Deploying test contract for amb ############\n"
cd "$DEPLOY_PATH"
node src/utils/deployTestBox.js
cd - > /dev/null
echo -e "\n\n############ Deploying amb stake erc to erc ############\n"
cp "$ENVS_PATH/amb-stake-erc-to-erc.env" "$DEPLOY_PATH/.env"
node deployMultiBridgeToken.js
node deployBridgeTokenRewardable.js
cd "$DEPLOY_PATH"
node deploy.js
cd - > /dev/null
node setupStakeTokens.js
cd - > /dev/null

View File

@@ -0,0 +1,43 @@
const path = require('path')
const { user } = require('../constants.json')
const contractsPath = '../../contracts'
require('dotenv').config({
path: path.join(__dirname, contractsPath, '/deploy/.env')
})
const { deployContract, sendRawTxHome, privateKeyToAddress } = require(`${contractsPath}/deploy/src/deploymentUtils`)
const { web3Home, deploymentPrivateKey } = require(`${contractsPath}/deploy/src/web3`)
const ERC677BridgeTokenRewardable = require(`${contractsPath}/build/contracts/ERC677BridgeTokenRewardable.json`)
const { DEPLOYMENT_ACCOUNT_PRIVATE_KEY } = process.env
const DEPLOYMENT_ACCOUNT_ADDRESS = privateKeyToAddress(DEPLOYMENT_ACCOUNT_PRIVATE_KEY)
async function deployBridgeTokenRewardable() {
try {
let homeNonce = await web3Home.eth.getTransactionCount(DEPLOYMENT_ACCOUNT_ADDRESS)
console.log('\n[Home] Deploying ERC677BridgeTokenRewardable Test token')
const stakeToken = await deployContract(ERC677BridgeTokenRewardable, ['STAKE', 'STAKE', '18', '77'], {
from: DEPLOYMENT_ACCOUNT_ADDRESS,
network: 'home',
nonce: homeNonce
})
homeNonce++
console.log('[Home] Stake Token: ', stakeToken.options.address)
const mintData = await stakeToken.methods
.mint(user.address, '500000000000000000000')
.encodeABI({ from: DEPLOYMENT_ACCOUNT_ADDRESS })
await sendRawTxHome({
data: mintData,
nonce: homeNonce,
to: stakeToken.options.address,
privateKey: deploymentPrivateKey,
url: process.env.HOME_RPC_URL
})
} catch (e) {
console.log(e)
throw e
}
}
deployBridgeTokenRewardable()

View File

@@ -0,0 +1,43 @@
const path = require('path')
const { user } = require('../constants.json')
const contractsPath = '../../contracts'
require('dotenv').config({
path: path.join(__dirname, contractsPath, '/deploy/.env')
})
const { deployContract, sendRawTxForeign, privateKeyToAddress } = require(`${contractsPath}/deploy/src/deploymentUtils`)
const { web3Foreign, deploymentPrivateKey } = require(`${contractsPath}/deploy/src/web3`)
const ERC677MultiBridgeToken = require(`${contractsPath}/build/contracts/ERC677MultiBridgeToken.json`)
const { DEPLOYMENT_ACCOUNT_PRIVATE_KEY } = process.env
const DEPLOYMENT_ACCOUNT_ADDRESS = privateKeyToAddress(DEPLOYMENT_ACCOUNT_PRIVATE_KEY)
async function deployMultiBridgeToken() {
try {
let foreignNonce = await web3Foreign.eth.getTransactionCount(DEPLOYMENT_ACCOUNT_ADDRESS)
console.log('\n[Foreign] Deploying ERC677MultiBridgeToken Test token')
const stakeToken = await deployContract(ERC677MultiBridgeToken, ['STAKE', 'STAKE', '18', '42'], {
from: DEPLOYMENT_ACCOUNT_ADDRESS,
network: 'foreign',
nonce: foreignNonce
})
foreignNonce++
console.log('[Foreign] Stake Token: ', stakeToken.options.address)
const mintData = await stakeToken.methods
.mint(user.address, '500000000000000000000')
.encodeABI({ from: DEPLOYMENT_ACCOUNT_ADDRESS })
await sendRawTxForeign({
data: mintData,
nonce: foreignNonce,
to: stakeToken.options.address,
privateKey: deploymentPrivateKey,
url: process.env.FOREIGN_RPC_URL
})
} catch (e) {
console.log(e)
throw e
}
}
deployMultiBridgeToken()

View File

@@ -0,0 +1,111 @@
const path = require('path')
const { ambStakeErcToErc, validator, secondValidator, thirdValidator } = require('../constants.json')
const contractsPath = '../../contracts'
require('dotenv').config({
path: path.join(__dirname, contractsPath, '/deploy/.env')
})
const { sendRawTxHome, sendRawTxForeign, privateKeyToAddress } = require(`${contractsPath}/deploy/src/deploymentUtils`)
const { web3Home, web3Foreign, deploymentPrivateKey } = require(`${contractsPath}/deploy/src/web3`)
const BlockReward = require(`${contractsPath}/build/contracts/BlockReward.json`)
const ERC677BridgeTokenRewardable = require(`${contractsPath}/build/contracts/ERC677BridgeTokenRewardable.json`)
const ERC677MultiBridgeToken = require(`${contractsPath}/build/contracts/ERC677MultiBridgeToken.json`)
const { DEPLOYMENT_ACCOUNT_PRIVATE_KEY } = process.env
const DEPLOYMENT_ACCOUNT_ADDRESS = privateKeyToAddress(DEPLOYMENT_ACCOUNT_PRIVATE_KEY)
async function setupStakeTokens() {
try {
let homeNonce = await web3Home.eth.getTransactionCount(DEPLOYMENT_ACCOUNT_ADDRESS)
const blockReward = new web3Home.eth.Contract(BlockReward.abi, ambStakeErcToErc.blockReward)
console.log('\n[Home] Set token in block reward')
const setTokenData = await blockReward.methods.setToken(ambStakeErcToErc.homeToken).encodeABI()
await sendRawTxHome({
data: setTokenData,
nonce: homeNonce,
to: blockReward.options.address,
privateKey: deploymentPrivateKey,
url: process.env.HOME_RPC_URL
})
homeNonce++
console.log('\n[Home] Set validators rewards in block reward')
const setValidatorsRewardsData = await blockReward.methods
.setValidatorsRewards([validator.address, secondValidator.address, thirdValidator.address])
.encodeABI()
await sendRawTxHome({
data: setValidatorsRewardsData,
nonce: homeNonce,
to: blockReward.options.address,
privateKey: deploymentPrivateKey,
url: process.env.HOME_RPC_URL
})
homeNonce++
const homeToken = new web3Home.eth.Contract(ERC677BridgeTokenRewardable.abi, ambStakeErcToErc.homeToken)
console.log('\n[Home] Set block reward in token')
const setBlockRewardData = await homeToken.methods.setBlockRewardContract(ambStakeErcToErc.blockReward).encodeABI()
await sendRawTxHome({
data: setBlockRewardData,
nonce: homeNonce,
to: homeToken.options.address,
privateKey: deploymentPrivateKey,
url: process.env.HOME_RPC_URL
})
homeNonce++
console.log('\n[Home] Add bridge in token')
const addBridgeData = await homeToken.methods.addBridge(ambStakeErcToErc.home).encodeABI()
await sendRawTxHome({
data: addBridgeData,
nonce: homeNonce,
to: homeToken.options.address,
privateKey: deploymentPrivateKey,
url: process.env.HOME_RPC_URL
})
homeNonce++
console.log('\n[Home] transfer token ownership to mediator')
const transferOwnershipData = await homeToken.methods.transferOwnership(ambStakeErcToErc.home).encodeABI()
await sendRawTxHome({
data: transferOwnershipData,
nonce: homeNonce,
to: homeToken.options.address,
privateKey: deploymentPrivateKey,
url: process.env.HOME_RPC_URL
})
homeNonce++
let foreignNonce = await web3Foreign.eth.getTransactionCount(DEPLOYMENT_ACCOUNT_ADDRESS)
const foreignToken = new web3Foreign.eth.Contract(ERC677MultiBridgeToken.abi, ambStakeErcToErc.foreignToken)
console.log('\n[Foreign] Add bridge in token')
const addBridgeForeignData = await homeToken.methods.addBridge(ambStakeErcToErc.foreign).encodeABI()
await sendRawTxForeign({
data: addBridgeForeignData,
nonce: foreignNonce,
to: foreignToken.options.address,
privateKey: deploymentPrivateKey,
url: process.env.FOREIGN_RPC_URL
})
foreignNonce++
console.log('\n[Foreign] transfer token ownership to mediator')
const transferOwnershipForeignData = await homeToken.methods.transferOwnership(ambStakeErcToErc.foreign).encodeABI()
await sendRawTxForeign({
data: transferOwnershipForeignData,
nonce: foreignNonce,
to: foreignToken.options.address,
privateKey: deploymentPrivateKey,
url: process.env.FOREIGN_RPC_URL
})
foreignNonce++
} catch (e) {
console.log(e)
throw e
}
}
setupStakeTokens()

View File

@@ -8,6 +8,9 @@ docker network create --driver bridge ultimate || true
docker-compose up -d parity1 parity2 e2e
startValidator () {
# make sure that old image tags are not cached
docker-compose $1 build
docker-compose $1 run -d --name $4 redis
docker-compose $1 run -d --name $5 rabbit
docker-compose $1 run $2 $3 -d oracle yarn watcher:signature-request
@@ -71,11 +74,12 @@ while [ "$1" != "" ]; do
fi
if [ "$1" == "ui" ]; then
docker-compose up -d ui ui-erc20 ui-erc20-native
docker-compose up -d ui ui-erc20 ui-erc20-native ui-amb-stake-erc20-erc20
docker-compose run -d -p 3000:3000 ui yarn start
docker-compose run -d -p 3001:3000 ui-erc20 yarn start
docker-compose run -d -p 3002:3000 ui-erc20-native yarn start
docker-compose run -d -p 3003:3000 ui-amb-stake-erc20-erc20 yarn start
fi
if [ "$1" == "deploy" ]; then
@@ -106,5 +110,9 @@ while [ "$1" != "" ]; do
../deployment-e2e/molecule.sh ultimate-amb
fi
if [ "$1" == "ultimate-amb-stake-erc-to-erc" ]; then
../deployment-e2e/molecule.sh ultimate-amb-stake-erc-to-erc
fi
shift # Shift all the parameters down by one
done

View File

@@ -31,6 +31,7 @@ async function checkWorker() {
const foreign = Object.assign({}, balances.foreign, events.foreign)
const status = Object.assign({}, balances, events, { home }, { foreign })
if (!status) throw new Error('status is empty: ' + JSON.stringify(status))
status.health = true
writeFile(`/responses/${MONITOR_BRIDGE_NAME}/getBalances.json`, status)
logger.debug('calling validators()')
@@ -59,6 +60,7 @@ async function checkWorker() {
}
vBalances.ok = vBalances.homeOk && vBalances.foreignOk
vBalances.health = true
writeFile(`/responses/${MONITOR_BRIDGE_NAME}/validators.json`, vBalances)
logger.debug('Done')
} catch (e) {

View File

@@ -16,12 +16,14 @@ async function checkWorker2() {
(evStats.onlyInForeignDeposits || evStats.home.processedMsgNotDeliveredInForeign).length === 0 &&
(evStats.onlyInHomeWithdrawals || evStats.foreign.deliveredMsgNotProcessedInHome).length === 0 &&
(evStats.onlyInForeignWithdrawals || evStats.foreign.processedMsgNotDeliveredInHome).length === 0
evStats.health = true
writeFile(`/responses/${MONITOR_BRIDGE_NAME}/eventsStats.json`, evStats)
logger.debug('calling alerts()')
const _alerts = await alerts()
if (!_alerts) throw new Error('alerts is empty: ' + JSON.stringify(_alerts))
_alerts.ok = !_alerts.executeAffirmations.mostRecentTxHash && !_alerts.executeSignatures.mostRecentTxHash
_alerts.health = true
writeFile(`/responses/${MONITOR_BRIDGE_NAME}/alerts.json`, _alerts)
logger.debug('Done x2')
} catch (e) {

View File

@@ -19,6 +19,7 @@ async function checkWorker3() {
const transfers = await stuckTransfers()
if (!transfers) throw new Error('transfers is empty: ' + JSON.stringify(transfers))
transfers.ok = transfers.total.length === 0
transfers.health = true
writeFile(`/responses/${MONITOR_BRIDGE_NAME}/stuckTransfers.json`, transfers)
logger.debug('Done')
}

View File

@@ -3,4 +3,6 @@ version: '2.4'
services:
monitor:
image: poanetwork/tokenbridge-monitor
build: .
build:
context: ..
dockerfile: monitor/Dockerfile

View File

@@ -3,7 +3,6 @@ const BN = require('bignumber.js')
const Web3 = require('web3')
const logger = require('./logger')('getBalances')
const { BRIDGE_MODES } = require('../commons')
const { blockNumberHalfDuplexDisabled } = require('./utils/tokenUtils')
const Web3Utils = Web3.utils
@@ -86,28 +85,7 @@ async function main(bridgeMode) {
const erc20Contract = new web3Foreign.eth.Contract(ERC20_ABI, erc20Address)
let investedAmountInDai = 0
let bridgeDsrBalance = 0
let foreignHalfDuplexErc20Balance = 0
let displayChaiToken = false
let displayHalfDuplexToken = false
let tokenSwapAllowed = false
try {
const halfDuplexTokenAddress = await foreignBridge.methods.halfDuplexErc20token().call()
if (halfDuplexTokenAddress !== erc20Address) {
const halfDuplexToken = new web3Foreign.eth.Contract(ERC20_ABI, halfDuplexTokenAddress)
logger.debug('calling halfDuplexToken.methods.balanceOf')
foreignHalfDuplexErc20Balance = await halfDuplexToken.methods
.balanceOf(COMMON_FOREIGN_BRIDGE_ADDRESS)
.call(null, blockNumberHalfDuplexDisabled)
logger.debug('getting last block numbers')
const block = await web3Foreign.eth.getBlock('latest')
logger.debug(`Checking if SCD Emergency Shutdown has happened`)
tokenSwapAllowed = await foreignBridge.methods.isTokenSwapAllowed(block.timestamp).call()
displayHalfDuplexToken = true
}
} catch (e) {
logger.debug('Methods for half duplex token are not present')
}
try {
logger.debug('calling foreignBridge.methods.isChaiTokenEnabled')
@@ -142,30 +120,16 @@ async function main(bridgeMode) {
const foreignErc20BalanceBN = new BN(foreignErc20Balance)
const investedAmountInDaiBN = new BN(investedAmountInDai)
const bridgeDsrBalanceBN = new BN(bridgeDsrBalance)
const halfDuplexErc20BalanceBN = displayHalfDuplexToken ? new BN(foreignHalfDuplexErc20Balance) : new BN(0)
const diff = foreignErc20BalanceBN
.plus(halfDuplexErc20BalanceBN)
.plus(investedAmountInDaiBN)
.minus(totalSupplyBN)
.toFixed()
let foreign = {
const foreign = {
erc20Balance: Web3Utils.fromWei(foreignErc20Balance)
}
if (displayHalfDuplexToken && tokenSwapAllowed) {
foreign = {
...foreign,
halfDuplexErc20Balance: Web3Utils.fromWei(foreignHalfDuplexErc20Balance)
}
} else if (displayHalfDuplexToken && !tokenSwapAllowed) {
foreign = {
...foreign,
halfDuplexErc20BalanceAfterES: Web3Utils.fromWei(foreignHalfDuplexErc20Balance)
}
}
if (displayChaiToken) {
foreign.investedErc20Balance = Web3Utils.fromWei(investedAmountInDai)
foreign.accumulatedInterest = Web3Utils.fromWei(bridgeDsrBalanceBN.minus(investedAmountInDaiBN).toString(10))

View File

@@ -1,11 +1,60 @@
#!/bin/bash
CONFIGDIR="configs"
RESPONSESDIR="responses"
IMAGETAG="latest"
cd $(dirname $0)/..
if /usr/local/bin/docker-compose ps | grep -q -i 'monitor'; then
for file in configs/*.env
tstart=`date +"%s"`
for file in ${CONFIGDIR}/*.env
do
docker run --rm --env-file $file -v $(pwd)/responses:/mono/monitor/responses poanetwork/tokenbridge-monitor:latest /bin/bash -c 'yarn check-all'
echo "${file} handling..."
bridgename=`source ${file} && echo ${MONITOR_BRIDGE_NAME}`
reportdir=${RESPONSESDIR}"/"${bridgename}
if [ ! -d ${reportdir} ]; then
mkdir -p ${reportdir}
fi
checksumfile=${bridgename}".shasum"
rm -f ${checksumfile}
for json in alerts.json eventsStats.json getBalances.json validators.json stuckTransfers.json; do
if [ -f ${reportdir}/${json} ]; then
shasum -a 256 ${reportdir}/${json} >> ${checksumfile}
fi
done
containername=${bridgename}"-checker"
docker container stats --no-stream ${containername} 2>/dev/null 1>&2
if [ ! "$?" == "0" ]; then
docker run --rm --env-file $file -v $(pwd)/${RESPONSESDIR}:/mono/monitor/responses \
--name ${containername} poanetwork/tokenbridge-monitor:${IMAGETAG} \
/bin/bash -c 'yarn check-all'
shasum -a 256 -s -c ${checksumfile}
if [ "$?" == "0" ]; then
echo "JSON files have not been updated - the monitor is not healthy"
for json in alerts.json eventsStats.json getBalances.json validators.json stuckTransfers.json; do
if [ -f ${reportdir}/${json} ]; then
echo '{"health": false, "lastChecked": '`date +"%s"`'}' > ${reportdir}/${json}
fi
done
else
echo "JSON files have been updated - new metrics collected"
fi
else
echo "${containername} have not finished yet" >&2
fi
rm ${checksumfile}
echo "========================================"
done
tend=`date +"%s"`
tdiff=`expr ${tend} - ${tstart}`
echo "Total time to run: ${tdiff}"
else
echo "Monitor is not running, skipping checks."
fi
fi

View File

@@ -114,6 +114,8 @@ async function main(mode) {
let directTransfers = transferEvents
const tokensSwappedAbiExists = FOREIGN_ABI.filter(e => e.type === 'event' && e.name === 'TokensSwapped')[0]
if (tokensSwappedAbiExists) {
logger.debug('collecting half duplex tokens participated in the bridge balance')
logger.debug("calling foreignBridge.getPastEvents('TokensSwapped')")
const tokensSwappedEvents = await getPastEvents(foreignBridge, {
event: 'TokensSwapped',
fromBlock: MONITOR_FOREIGN_START_BLOCK,
@@ -128,7 +130,7 @@ async function main(mode) {
// Exclude chai token from previous erc20
try {
logger.debug('calling foreignBridge.chaiToken()')
logger.debug('calling foreignBridge.chaiToken() to remove it from half duplex tokens list')
const chaiToken = await foreignBridge.methods.chaiToken().call()
uniqueTokenAddressesSet.delete(chaiToken)
} catch (e) {
@@ -136,7 +138,7 @@ async function main(mode) {
}
// Exclude dai token from previous erc20
try {
logger.debug('calling foreignBridge.erc20token()')
logger.debug('calling foreignBridge.erc20token() to remove it from half duplex tokens list')
const daiToken = await foreignBridge.methods.erc20token().call()
uniqueTokenAddressesSet.delete(daiToken)
} catch (e) {
@@ -148,6 +150,8 @@ async function main(mode) {
uniqueTokenAddresses.map(async tokenAddress => {
const halfDuplexTokenContract = new web3Foreign.eth.Contract(ERC20_ABI, tokenAddress)
logger.debug('Half duplex token:', tokenAddress)
logger.debug("calling halfDuplexTokenContract.getPastEvents('Transfer')")
const halfDuplexTransferEvents = (await getPastEvents(halfDuplexTokenContract, {
event: 'Transfer',
fromBlock: MONITOR_FOREIGN_START_BLOCK,
@@ -158,6 +162,7 @@ async function main(mode) {
})).map(normalizeEvent)
// Remove events after the ES
logger.debug('filtering half duplex transfers happened before ES')
const validHalfDuplexTransfers = await filterTransferBeforeES(halfDuplexTransferEvents)
transferEvents = [...validHalfDuplexTransfers, ...transferEvents]

View File

@@ -1,14 +1,9 @@
const web3Utils = require('web3').utils
const { addTxHashToData, parseAMBMessage } = require('../../commons')
const { parseAMBMessage } = require('../../commons')
function deliveredMsgNotProcessed(processedList) {
return deliveredMsg => {
const msg = parseAMBMessage(
addTxHashToData({
encodedData: deliveredMsg.returnValues.encodedData,
transactionHash: deliveredMsg.transactionHash
})
)
const msg = parseAMBMessage(deliveredMsg.returnValues.encodedData)
return (
processedList.filter(processedMsg => {
return messageEqualsEvent(msg, processedMsg.returnValues)
@@ -21,12 +16,7 @@ function processedMsgNotDelivered(deliveredList) {
return processedMsg => {
return (
deliveredList.filter(deliveredMsg => {
const msg = parseAMBMessage(
addTxHashToData({
encodedData: deliveredMsg.returnValues.encodedData,
transactionHash: deliveredMsg.transactionHash
})
)
const msg = parseAMBMessage(deliveredMsg.returnValues.encodedData)
return messageEqualsEvent(msg, processedMsg.returnValues)
}).length === 0
)
@@ -37,7 +27,7 @@ function messageEqualsEvent(parsedMsg, event) {
return (
web3Utils.toChecksumAddress(parsedMsg.sender) === event.sender &&
web3Utils.toChecksumAddress(parsedMsg.executor) === event.executor &&
parsedMsg.txHash === event.transactionHash
parsedMsg.messageId === event.messageId
)
}

View File

@@ -14,6 +14,7 @@
"worker:convert-to-chai": "./scripts/start-worker.sh worker convert-to-chai-worker",
"sender:home": "./scripts/start-worker.sh sender home-sender",
"sender:foreign": "./scripts/start-worker.sh sender foreign-sender",
"confirm:transfer": "./scripts/start-worker.sh confirmRelay transfer-watcher",
"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",

View File

@@ -8,11 +8,12 @@ LOGS_DIR="logs/"
WORKER="${WORKERS_DIR}${1}.js"
CONFIG="${2}.config.js"
LOG="${LOGS_DIR}${2}.txt"
TX_HASH="${3}"
CHECKS=$(node scripts/initialChecks.js)
if [ "${NODE_ENV}" = "production" ]; then
exec node "${WORKER}" "${CONFIG}" "$CHECKS"
exec node "${WORKER}" "${CONFIG}" "$CHECKS" "$TX_HASH"
else
node "${WORKER}" "${CONFIG}" "$CHECKS" | tee -a "${LOG}" | pino-pretty
node "${WORKER}" "${CONFIG}" "$CHECKS" "$TX_HASH" | tee -a "${LOG}" | pino-pretty
fi

186
oracle/src/confirmRelay.js Normal file
View File

@@ -0,0 +1,186 @@
require('../env')
const path = require('path')
const { isAttached, connectWatcherToQueue, connection } = require('./services/amqpClient')
const logger = require('./services/logger')
const GasPrice = require('./services/gasPrice')
const rpcUrlsManager = require('./services/getRpcUrlsManager')
const { getNonce, getChainId, getEventsFromTx } = require('./tx/web3')
const { sendTx } = require('./tx/sendTx')
const { checkHTTPS, watchdog, syncForEach, addExtraGas } = require('./utils/utils')
const { EXIT_CODES, EXTRA_GAS_PERCENTAGE } = require('./utils/constants')
const { ORACLE_VALIDATOR_ADDRESS, ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY, ORACLE_ALLOW_HTTP_FOR_RPC } = process.env
if (process.argv.length < 5) {
logger.error('Please check the number of arguments, transaction hash is not present')
process.exit(EXIT_CODES.GENERAL_ERROR)
}
const config = require(path.join('../config/', process.argv[2]))
const txHash = process.argv[4]
const processSignatureRequests = require('./events/processSignatureRequests')(config)
const processCollectedSignatures = require('./events/processCollectedSignatures')(config)
const processAffirmationRequests = require('./events/processAffirmationRequests')(config)
const processTransfers = require('./events/processTransfers')(config)
const processAMBSignatureRequests = require('./events/processAMBSignatureRequests')(config)
const processAMBCollectedSignatures = require('./events/processAMBCollectedSignatures')(config)
const processAMBAffirmationRequests = require('./events/processAMBAffirmationRequests')(config)
const web3Instance = config.web3
const { eventContractAddress } = config
const eventContract = new web3Instance.eth.Contract(config.eventAbi, eventContractAddress)
let attached
async function initialize() {
try {
const checkHttps = checkHTTPS(ORACLE_ALLOW_HTTP_FOR_RPC, logger)
rpcUrlsManager.homeUrls.forEach(checkHttps('home'))
rpcUrlsManager.foreignUrls.forEach(checkHttps('foreign'))
attached = await isAttached()
if (attached) {
logger.info('RabbitMQ container is available, using oracle sender')
} else {
logger.info('RabbitMQ container is not available, using internal sender')
}
connectWatcherToQueue({
queueName: config.queue,
workerQueue: config.workerQueue,
cb: runMain
})
} catch (e) {
logger.error(e)
process.exit(EXIT_CODES.GENERAL_ERROR)
}
}
async function runMain({ sendToQueue }) {
try {
const sendJob = attached ? sendToQueue : sendJobTx
if (!attached || connection.isConnected()) {
if (config.maxProcessingTime) {
await watchdog(() => main({ sendJob, txHash }), config.maxProcessingTime, () => {
logger.fatal('Max processing time reached')
process.exit(EXIT_CODES.MAX_TIME_REACHED)
})
} else {
await main({ sendJob, txHash })
}
} else {
setTimeout(() => {
runMain({ sendToQueue })
}, config.pollingInterval)
}
} catch (e) {
logger.error(e)
}
}
function processEvents(events) {
switch (config.id) {
case 'native-erc-signature-request':
case 'erc-erc-signature-request':
case 'erc-native-signature-request':
return processSignatureRequests(events)
case 'native-erc-collected-signatures':
case 'erc-erc-collected-signatures':
case 'erc-native-collected-signatures':
return processCollectedSignatures(events)
case 'native-erc-affirmation-request':
case 'erc677-erc677-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)
case 'amb-collected-signatures':
return processAMBCollectedSignatures(events)
case 'amb-affirmation-request':
return processAMBAffirmationRequests(events)
default:
return []
}
}
async function main({ sendJob, txHash }) {
try {
const events = await getEventsFromTx({
web3: web3Instance,
contract: eventContract,
event: config.event,
txHash,
filter: config.eventFilter
})
logger.info(`Found ${events.length} ${config.event} events`)
if (events.length) {
const job = await processEvents(events)
logger.info('Transactions to send:', job.length)
if (job.length) {
await sendJob(job)
}
}
} catch (e) {
logger.error(e)
}
await connection.close()
logger.debug('Finished')
}
async function sendJobTx(jobs) {
const gasPrice = await GasPrice.start(config.queue, true)
const chainId = await getChainId(config.queue)
let nonce = await getNonce(web3Instance, ORACLE_VALIDATOR_ADDRESS)
await syncForEach(jobs, async job => {
const gasLimit = addExtraGas(job.gasEstimate, EXTRA_GAS_PERCENTAGE)
try {
logger.info(`Sending transaction with nonce ${nonce}`)
const txHash = await sendTx({
chain: config.queue,
data: job.data,
nonce,
gasPrice: gasPrice.toString(10),
amount: '0',
gasLimit,
privateKey: ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY,
to: job.to,
chainId,
web3: web3Instance
})
nonce++
logger.info(
{ eventTransactionHash: job.transactionReference, generatedTransactionHash: txHash },
`Tx generated ${txHash} for event Tx ${job.transactionReference}`
)
} catch (e) {
logger.error(
{ eventTransactionHash: job.transactionReference, error: e.message },
`Tx Failed for event Tx ${job.transactionReference}.`,
e.message
)
if (e.message.includes('Insufficient funds')) {
const currentBalance = await web3Instance.eth.getBalance(ORACLE_VALIDATOR_ADDRESS)
const minimumBalance = gasLimit.multipliedBy(gasPrice)
logger.error(
`Insufficient funds: ${currentBalance}. Stop processing messages until the balance is at least ${minimumBalance}.`
)
}
}
})
}
initialize()

View File

@@ -6,7 +6,7 @@ const { web3Home } = require('../../services/web3')
const bridgeValidatorsABI = require('../../../../contracts/build/contracts/BridgeValidators').abi
const { EXIT_CODES, MAX_CONCURRENT_EVENTS } = require('../../utils/constants')
const estimateGas = require('./estimateGas')
const { addTxHashToData, parseAMBMessage } = require('../../../../commons')
const { parseAMBMessage } = require('../../../../commons')
const { AlreadyProcessedError, AlreadySignedError, InvalidValidatorError } = require('../../utils/errors')
const limit = promiseLimit(MAX_CONCURRENT_EVENTS)
@@ -30,20 +30,16 @@ function processAffirmationRequestsBuilder(config) {
rootLogger.debug(`Processing ${affirmationRequests.length} AffirmationRequest events`)
const callbacks = affirmationRequests
.map(affirmationRequest => async () => {
const { encodedData } = affirmationRequest.returnValues
const { messageId, encodedData: message } = affirmationRequest.returnValues
const logger = rootLogger.child({
eventTransactionHash: affirmationRequest.transactionHash
})
const message = addTxHashToData({
encodedData,
transactionHash: affirmationRequest.transactionHash
eventTransactionHash: affirmationRequest.transactionHash,
eventMessageId: messageId
})
const { sender, executor } = parseAMBMessage(message)
logger.info({ sender, executor }, `Processing affirmationRequest ${affirmationRequest.transactionHash}`)
logger.info({ sender, executor }, `Processing affirmationRequest ${messageId}`)
let gasEstimate
try {
@@ -63,12 +59,10 @@ function processAffirmationRequestsBuilder(config) {
logger.fatal({ address: config.validatorAddress }, 'Invalid validator')
process.exit(EXIT_CODES.INCOMPATIBILITY)
} else if (e instanceof AlreadySignedError) {
logger.info(`Already signed affirmationRequest ${affirmationRequest.transactionHash}`)
logger.info(`Already signed affirmationRequest ${messageId}`)
return
} else if (e instanceof AlreadyProcessedError) {
logger.info(
`affirmationRequest ${affirmationRequest.transactionHash} was already processed by other validators`
)
logger.info(`affirmationRequest ${messageId} was already processed by other validators`)
return
} else {
logger.error(e, 'Unknown error while processing transaction')

View File

@@ -17,7 +17,7 @@ async function estimateGas({
r,
s,
signatures,
txHash,
messageId,
address
}) {
try {
@@ -32,7 +32,7 @@ async function estimateGas({
// check if the message was already processed
logger.debug('Check if the message was already processed')
const alreadyProcessed = await foreignBridge.methods.relayedMessages(txHash).call()
const alreadyProcessed = await foreignBridge.methods.relayedMessages(messageId).call()
if (alreadyProcessed) {
throw new AlreadyProcessedError()
}

View File

@@ -69,7 +69,8 @@ function processCollectedSignaturesBuilder(config) {
await Promise.all(signaturePromises)
const signatures = packSignatures(signaturesArray)
const { txHash } = parseAMBMessage(message)
const { messageId } = parseAMBMessage(message)
logger.info(`Processing messageId: ${messageId}`)
let gasEstimate
try {
@@ -83,7 +84,7 @@ function processCollectedSignaturesBuilder(config) {
signatures,
message,
numberOfCollectedSignatures: NumberOfCollectedSignatures,
txHash,
messageId,
address: config.validatorAddress
})
logger.debug({ gasEstimate }, 'Gas estimated')

View File

@@ -4,7 +4,7 @@ const { HttpListProviderError } = require('http-list-provider')
const bridgeValidatorsABI = require('../../../../contracts/build/contracts/BridgeValidators').abi
const rootLogger = require('../../services/logger')
const { web3Home } = require('../../services/web3')
const { addTxHashToData, parseAMBMessage } = require('../../../../commons')
const { parseAMBMessage } = require('../../../../commons')
const estimateGas = require('../processSignatureRequests/estimateGas')
const { AlreadyProcessedError, AlreadySignedError, InvalidValidatorError } = require('../../utils/errors')
const { EXIT_CODES, MAX_CONCURRENT_EVENTS } = require('../../utils/constants')
@@ -32,19 +32,15 @@ function processSignatureRequestsBuilder(config) {
rootLogger.debug(`Processing ${signatureRequests.length} SignatureRequest events`)
const callbacks = signatureRequests
.map(signatureRequest => async () => {
const { encodedData } = signatureRequest.returnValues
const { messageId, encodedData: message } = signatureRequest.returnValues
const logger = rootLogger.child({
eventTransactionHash: signatureRequest.transactionHash
})
const message = addTxHashToData({
encodedData,
transactionHash: signatureRequest.transactionHash
eventTransactionHash: signatureRequest.transactionHash,
eventMessageId: messageId
})
const { sender, executor } = parseAMBMessage(message)
logger.info({ sender, executor }, `Processing signatureRequest ${signatureRequest.transactionHash}`)
logger.info({ sender, executor }, `Processing signatureRequest ${messageId}`)
const signature = web3Home.eth.accounts.sign(message, `0x${ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY}`)
@@ -67,12 +63,10 @@ function processSignatureRequestsBuilder(config) {
logger.fatal({ address: config.validatorAddress }, 'Invalid validator')
process.exit(EXIT_CODES.INCOMPATIBILITY)
} else if (e instanceof AlreadySignedError) {
logger.info(`Already signed signatureRequest ${signatureRequest.transactionHash}`)
logger.info(`Already signed signatureRequest ${messageId}`)
return
} else if (e instanceof AlreadyProcessedError) {
logger.info(
`signatureRequest ${signatureRequest.transactionHash} was already processed by other validators`
)
logger.info(`signatureRequest ${messageId} was already processed by other validators`)
return
} else {
logger.error(e, 'Unknown error while processing transaction')

View File

@@ -1,4 +1,6 @@
require('../../env')
const url = require('url')
const dns = require('dns')
const connection = require('amqp-connection-manager').connect(process.env.ORACLE_QUEUE_URL)
const logger = require('./logger')
const { getRetrySequence } = require('../utils/utils')
@@ -11,6 +13,14 @@ connection.on('disconnect', () => {
logger.error('Disconnected from amqp Broker')
})
async function isAttached() {
if (!process.env.ORACLE_QUEUE_URL) {
return false
}
const amqpHost = new url.URL(process.env.ORACLE_QUEUE_URL).hostname
return new Promise(res => dns.lookup(amqpHost, err => res(err === null)))
}
function connectWatcherToQueue({ queueName, workerQueue, cb }) {
const queueList = workerQueue ? [queueName, workerQueue] : [queueName]
@@ -119,6 +129,7 @@ async function generateRetry({ data, msgRetries, channelWrapper, channel, queueN
}
module.exports = {
isAttached,
connectWatcherToQueue,
connectSenderToQueue,
connectWorkerToQueue,

View File

@@ -45,7 +45,7 @@ const fetchGasPrice = async (speedType, factor, bridgeContract, gasPriceSupplier
return cachedGasPrice
}
async function start(chainId) {
async function start(chainId, fetchOnce) {
clearInterval(fetchGasPriceInterval)
let bridgeContract = null
@@ -73,10 +73,16 @@ async function start(chainId) {
throw new Error(`Unrecognized chainId '${chainId}'`)
}
if (fetchOnce) {
await fetchGasPrice(speedType, factor, bridgeContract, () => fetch(gasPriceSupplierUrl))
return getPrice()
}
fetchGasPriceInterval = setIntervalAndRun(
() => fetchGasPrice(speedType, factor, bridgeContract, () => fetch(gasPriceSupplierUrl)),
updateInterval
)
return null
}
function getPrice() {

View File

@@ -69,10 +69,34 @@ async function getEvents({ contract, event, fromBlock, toBlock, filter }) {
}
}
async function getEventsFromTx({ web3, contract, event, txHash, filter }) {
try {
const contractAddress = contract.options.address
logger.info({ contractAddress, event, txHash }, 'Getting past events for specific transaction')
const { logs } = await web3.eth.getTransactionReceipt(txHash)
const eventAbi = contract.options.jsonInterface.find(abi => abi.name === event)
const decodeAbi = contract._decodeEventABI.bind(eventAbi)
const pastEvents = logs
.filter(event => event.topics[0] === eventAbi.signature)
.map(decodeAbi)
.filter(event =>
eventAbi.inputs.every(arg => {
const encodeParam = param => web3.eth.abi.encodeParameter(arg.type, param)
return !filter[arg.name] || encodeParam(filter[arg.name]) === encodeParam(event.returnValues[arg.name])
})
)
logger.debug({ contractAddress, event, count: pastEvents.length }, 'Past events obtained')
return pastEvents
} catch (e) {
throw new Error(`${event} events cannot be obtained`)
}
}
module.exports = {
getNonce,
getBlockNumber,
getChainId,
getRequiredBlockConfirmations,
getEvents
getEvents,
getEventsFromTx
}

View File

@@ -7,18 +7,18 @@
"license": "MIT",
"private": true,
"devDependencies": {
"wsrun": "3.6.5",
"eslint-config-prettier": "3.1.0",
"eslint": "5.16.0",
"eslint-config-airbnb": "17.1.0",
"eslint-config-prettier": "3.1.0",
"eslint-plugin-flowtype": "^3.8.1",
"eslint-plugin-jsx-a11y": "^6.2.1",
"eslint-plugin-node": "7.0.1",
"eslint-plugin-prettier": "2.6.2",
"eslint-plugin-react": "^7.13.0",
"eslint-plugin-react-hooks": "^1.6.0",
"eslint-plugin-flowtype": "^3.8.1",
"mocha": "^5.2.0",
"prettier": "1.14.3",
"mocha": "^5.2.0"
"wsrun": "3.6.5"
},
"workspaces": [
"commons",

View File

@@ -19,6 +19,7 @@
"maxCodeSize": 24576,
"maxCodeSizeTransition": "0x0",
"eip140Transition": "0x0",
"eip145Transition": "0x0",
"eip211Transition": "0x0",
"eip214Transition": "0x0",
"eip658Transition": "0x0",

View File

@@ -19,6 +19,7 @@
"maxCodeSize": 24576,
"maxCodeSizeTransition": "0x0",
"eip140Transition": "0x0",
"eip145Transition": "0x0",
"eip211Transition": "0x0",
"eip214Transition": "0x0",
"eip658Transition": "0x0",

View File

@@ -5,6 +5,7 @@ const {
nativeToErcBridge,
ercToErcBridge,
ercToNativeBridge,
ambStakeErcToErc,
homeRPC,
foreignRPC
} = require('../e2e-commons/constants.json')
@@ -38,6 +39,10 @@ class Utils {
return ercToNativeBridge.ui
}
static async getAMBStakeStartURL() {
return ambStakeErcToErc.ui
}
static async startBrowserWithMetamask() {
const source = './MetaMask.crx'
const options = new chrome.Options()

View File

@@ -2,7 +2,7 @@ const { By } = require('selenium-webdriver/lib/by')
const { Page } = require('./Page.js')
const fieldAmount = By.id('amount')
const buttonTransfer = By.className('bridge-form-button ')
const buttonTransfer = By.className('bridge-form-button')
const buttonOk = By.className('swal-button swal-button--confirm')
const fieldsBalance = By.className('network-balance')
const classWeb3Loaded = By.className('web3-loaded')

View File

@@ -308,4 +308,94 @@ test.describe('e2e-test for bridge.poa, version 1.5.0', async function() {
return await assert.strictEqual(result, true, 'Test FAILED. Foreign POA balance is not correct after transaction')
})
})
test.describe('AMB-STAKE-ERC-TO-ERC', async () => {
test.it('User is able to open main page of bridge-ui ', async () => {
startURL = await Utils.getAMBStakeStartURL()
const result = await mainPage.open(startURL)
console.log('Test URL: ' + startURL)
return await assert.strictEqual(result, true, 'Test FAILED. Build failed.')
})
test.it('Home page: disclaimer is displayed ', async () => {
const result = await mainPage.confirmDisclaimer()
return await assert.strictEqual(result, true, 'Test FAILED. Disclaimer is not displayed')
})
test.it('Main page: foreign erc20 balance is displayed', async () => {
await foreignAccount.setMetaMaskNetwork()
foreignBalanceBefore = await mainPage.getForeignPOABalance()
console.log('foreignBalanceBefore = ' + foreignBalanceBefore)
const result = foreignBalanceBefore !== 0
return await assert.strictEqual(result, true, 'Test FAILED. Foreign erc20 balance is zero')
})
test.it('Main page: home erc20 balance is displayed', async () => {
homeBalanceBefore = await mainPage.getHomePOABalance()
console.log('homeBalanceBefore = ' + homeBalanceBefore)
const result = homeBalanceBefore !== 0
return await assert.strictEqual(result, true, 'Test FAILED. Home erc20 balance is zero or not displayed ')
})
test.it('User is able to send tokens from Foreign account to Home account', async () => {
homeBalanceBefore = await mainPage.getForeignPOABalance()
foreignBalanceBefore = await mainPage.getHomePOABalance()
const result = await foreignAccount.transferTokens(maxAmountPerTransactionLimit)
return await assert.strictEqual(
result,
true,
'Test FAILED. User is able send tokens from Foreign account to Home account'
)
})
test.it('Foreign POA balance has correctly changed after transaction', async () => {
const newForeignBalance = await mainPage.getHomePOABalance()
const shouldBe = foreignBalanceBefore - maxAmountPerTransactionLimit
console.log('newForeignBalance = ' + newForeignBalance)
console.log('shouldBe = ' + shouldBe)
const result = Math.abs(shouldBe - newForeignBalance) < maxAmountPerTransactionLimit / 100
return await assert.strictEqual(result, true, 'Test FAILED.Foreign POA balance is not correct after transaction')
})
test.it('Home account has received correct amount of tokens after transaction', async () => {
const newHomeBalance = await mainPage.getForeignPOABalance()
const shouldBe = homeBalanceBefore + maxAmountPerTransactionLimit
console.log('newHomeBalance = ' + newHomeBalance)
console.log('shouldBe = ' + shouldBe)
const result = Math.abs(shouldBe - newHomeBalance) < maxAmountPerTransactionLimit / 100
return await assert.strictEqual(result, true, 'Test FAILED.Home POA balance is not correct after transaction')
})
test.it('User is able to send tokens from Home account to Foreign account', async () => {
await homeAccount.setMetaMaskNetwork()
homeBalanceBefore = await mainPage.getHomePOABalance()
foreignBalanceBefore = await mainPage.getForeignPOABalance()
const result = await homeAccount.transferTokens(maxAmountPerTransactionLimit)
return await assert.strictEqual(
result,
true,
'Test FAILED. User is able send tokens from Home account to Foreign account'
)
})
test.it('Home POA balance has correctly changed after transaction', async () => {
const newHomeBalance = await mainPage.getHomePOABalance()
const shouldBe = homeBalanceBefore - maxAmountPerTransactionLimit
console.log('newHomeBalance = ' + newHomeBalance)
console.log('shouldBe = ' + shouldBe)
const result = Math.abs(shouldBe - newHomeBalance) < maxAmountPerTransactionLimit / 100
homeBalanceBefore = newHomeBalance
return await assert.strictEqual(result, true, 'Test FAILED.Home POA balance is not correct after transaction')
})
test.it('Foreign account has received correct amount of tokens after transaction', async () => {
const newForeignBalance = await mainPage.getForeignPOABalance()
const shouldBe = foreignBalanceBefore + maxAmountPerTransactionLimit
console.log('newForeignBalance = ' + newForeignBalance)
console.log('shouldBe = ' + shouldBe)
const result = Math.abs(shouldBe - newForeignBalance) < maxAmountPerTransactionLimit / 100
return await assert.strictEqual(result, true, 'Test FAILED. Foreign POA balance is not correct after transaction')
})
})
})

View File

@@ -32,11 +32,15 @@ COMMON_FOREIGN_GAS_PRICE_FACTOR=1
# Default
UI_TITLE=TokenBridge UI app - %c
UI_OG_TITLE=POA Bridge UI
UI_DESCRIPTION=The POA cross-chain bridge serves as a method of transferring POA native tokens from the POA Network to the Ethereum network in a quick and cost-efficient manner.
UI_PORT=3000
UI_PUBLIC_URL=https://bridge.poa.net
# RSK
#UI_DESCRIPTION=The TokenBridge serves as a method of transferring Bancor Network tokens between the Ethereum network and the Rootstock network in a quick and cost-efficient manner.
# To use Ethereum-classic styles
#UI_STYLES=classic
# To use Ethereum-classic styles: classic
# To use STAKE styles: stake
# To use poa core styles: core
UI_STYLES=core

View File

@@ -7,6 +7,7 @@
"bignumber.js": "^6.0.0",
"coveralls": "^3.0.0",
"customize-cra": "^0.2.12",
"date-fns": "^2.13.0",
"dotenv": "^7.0.0",
"fs-extra": "^5.0.0",
"mobx": "^4.0.2",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

BIN
ui/public/favicons/core.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

View File

@@ -15,4 +15,4 @@
"theme_color": "#ffffff",
"background_color": "#ffffff",
"display": "standalone"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 144 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 852 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 852 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

View File

@@ -5,14 +5,13 @@
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="theme-color" content="#000000">
<meta property="og:title" content="POA Bridge UI" />
<meta property="og:title" content="%REACT_APP_UI_OG_TITLE%" />
<meta property="og:description" content="%REACT_APP_UI_DESCRIPTION%" />
<meta property="og:url" content="https://poanetwork.github.io/bridge-ui" />
<meta property="og:url" content="%REACT_APP_UI_PUBLIC_URL%" />
<meta property="og:type" content="website" />
<meta property="og:image" content="/images/bridgeogimage.jpg">
<link href="https://fonts.googleapis.com/css?family=Nunito:300,400,700" rel="stylesheet">
<link rel="manifest" href="%PUBLIC_URL%/favicons/manifest.json">
<link rel="shortcut icon" href="%PUBLIC_URL%/favicons/favicon.ico">
<meta property="og:image" content="%REACT_APP_UI_PUBLIC_URL%/images/ogimage-%REACT_APP_UI_STYLES%.png">
<link rel="manifest" href="/favicons/manifest.json">
<link rel="shortcut icon" href="/favicons/%REACT_APP_UI_STYLES%.ico">
<title>TokenBridge UI app</title>
</head>

View File

@@ -6,12 +6,7 @@ const fs = require('fs')
const stylePath = path.resolve(__dirname, '..', 'src', 'assets', 'stylesheets')
const destinationFilename = 'application.css'
let filename
if (process.env.UI_STYLES === 'classic') {
filename = 'application.classic.css'
} else {
filename = 'application.core.css'
}
const theme = process.env.UI_STYLES || 'core'
const filename = `application.${theme}.css`
fs.copyFileSync(path.resolve(stylePath, filename), path.resolve(stylePath, destinationFilename))

View File

@@ -0,0 +1,7 @@
<svg width="220" height="105" viewBox="0 0 220 105" fill="none" xmlns="http://www.w3.org/2000/svg">
<mask id="path-1-inside-1" fill="white">
<path fill-rule="evenodd" clip-rule="evenodd" d="M112.828 1.85788C111.266 0.295785 108.734 0.295786 107.172 1.85788L100.029 9.00001H4C1.79086 9.00001 0 10.7909 0 13V101C0 103.209 1.79086 105 4 105H216C218.209 105 220 103.209 220 101V13C220 10.7909 218.209 9.00001 216 9.00001H119.971L112.828 1.85788Z"/>
</mask>
<path fill-rule="evenodd" clip-rule="evenodd" d="M112.828 1.85788C111.266 0.295785 108.734 0.295786 107.172 1.85788L100.029 9.00001H4C1.79086 9.00001 0 10.7909 0 13V101C0 103.209 1.79086 105 4 105H216C218.209 105 220 103.209 220 101V13C220 10.7909 218.209 9.00001 216 9.00001H119.971L112.828 1.85788Z" fill="white"/>
<path d="M107.172 1.85788L106.464 1.15077V1.15078L107.172 1.85788ZM112.828 1.85788L113.536 1.15078V1.15077L112.828 1.85788ZM100.029 9.00001V10H100.444L100.737 9.70711L100.029 9.00001ZM119.971 9.00001L119.263 9.70711L119.556 10H119.971V9.00001ZM107.879 2.56499C109.05 1.39342 110.95 1.39342 112.121 2.56499L113.536 1.15077C111.583 -0.801845 108.417 -0.801845 106.464 1.15077L107.879 2.56499ZM100.737 9.70711L107.879 2.56499L106.464 1.15078L99.3223 8.2929L100.737 9.70711ZM4 10H100.029V8.00001H4V10ZM1 13C1 11.3432 2.34315 10 4 10V8.00001C1.23858 8.00001 -1 10.2386 -1 13H1ZM1 101V13H-1V101H1ZM4 104C2.34314 104 1 102.657 1 101H-1C-1 103.761 1.23857 106 4 106V104ZM216 104H4V106H216V104ZM219 101C219 102.657 217.657 104 216 104V106C218.761 106 221 103.761 221 101H219ZM219 13V101H221V13H219ZM216 10C217.657 10 219 11.3432 219 13H221C221 10.2386 218.761 8.00001 216 8.00001V10ZM119.971 10H216V8.00001H119.971V10ZM112.121 2.56499L119.263 9.70711L120.678 8.2929L113.536 1.15078L112.121 2.56499Z" fill="#E6ECF1" mask="url(#path-1-inside-1)"/>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -0,0 +1,7 @@
<svg width="127" height="89" viewBox="0 0 127 89" fill="none" xmlns="http://www.w3.org/2000/svg">
<mask id="path-1-inside-1" fill="white">
<path fill-rule="evenodd" clip-rule="evenodd" d="M65.8284 1.17157C64.2663 -0.390524 61.7337 -0.390524 60.1716 1.17157L53.3431 8.00001H4C1.79086 8.00001 0 9.79086 0 12V85C0 87.2091 1.79086 89 4 89H123C125.209 89 127 87.2092 127 85V12C127 9.79087 125.209 8.00001 123 8.00001H72.6569L65.8284 1.17157Z"/>
</mask>
<path fill-rule="evenodd" clip-rule="evenodd" d="M65.8284 1.17157C64.2663 -0.390524 61.7337 -0.390524 60.1716 1.17157L53.3431 8.00001H4C1.79086 8.00001 0 9.79086 0 12V85C0 87.2091 1.79086 89 4 89H123C125.209 89 127 87.2092 127 85V12C127 9.79087 125.209 8.00001 123 8.00001H72.6569L65.8284 1.17157Z" fill="white"/>
<path d="M60.1716 1.17157L60.8787 1.87868V1.87868L60.1716 1.17157ZM65.8284 1.17157L66.5355 0.464466V0.464465L65.8284 1.17157ZM53.3431 8.00001V9.00001H53.7574L54.0502 8.70711L53.3431 8.00001ZM72.6569 8.00001L71.9498 8.70711L72.2426 9.00001H72.6569V8.00001ZM60.8787 1.87868C62.0503 0.707108 63.9497 0.707105 65.1213 1.87868L66.5355 0.464465C64.5829 -1.48815 61.4171 -1.48816 59.4645 0.464467L60.8787 1.87868ZM54.0502 8.70711L60.8787 1.87868L59.4645 0.464466L52.636 7.2929L54.0502 8.70711ZM4 9.00001H53.3431V7.00001H4V9.00001ZM1 12C1 10.3431 2.34314 9.00001 4 9.00001V7.00001C1.23858 7.00001 -1 9.23858 -1 12H1ZM1 85V12H-1V85H1ZM4 88C2.34314 88 1 86.6569 1 85H-1C-1 87.7614 1.23857 90 4 90V88ZM123 88H4V90H123V88ZM126 85C126 86.6569 124.657 88 123 88V90C125.761 90 128 87.7614 128 85H126ZM126 12V85H128V12H126ZM123 9.00001C124.657 9.00001 126 10.3432 126 12H128C128 9.23858 125.761 7.00001 123 7.00001V9.00001ZM72.6569 9.00001H123V7.00001H72.6569V9.00001ZM65.1213 1.87868L71.9498 8.70711L73.364 7.2929L66.5355 0.464466L65.1213 1.87868Z" fill="#E6ECF1" mask="url(#path-1-inside-1)"/>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 B

View File

@@ -0,0 +1,7 @@
<svg width="220" height="105" viewBox="0 0 220 105" fill="none" xmlns="http://www.w3.org/2000/svg">
<mask id="path-1-inside-1" fill="white">
<path fill-rule="evenodd" clip-rule="evenodd" d="M112.828 1.85788C111.266 0.295785 108.734 0.295786 107.172 1.85788L100.029 9.00001H4C1.79086 9.00001 0 10.7909 0 13V101C0 103.209 1.79086 105 4 105H216C218.209 105 220 103.209 220 101V13C220 10.7909 218.209 9.00001 216 9.00001H119.971L112.828 1.85788Z"/>
</mask>
<path fill-rule="evenodd" clip-rule="evenodd" d="M112.828 1.85788C111.266 0.295785 108.734 0.295786 107.172 1.85788L100.029 9.00001H4C1.79086 9.00001 0 10.7909 0 13V101C0 103.209 1.79086 105 4 105H216C218.209 105 220 103.209 220 101V13C220 10.7909 218.209 9.00001 216 9.00001H119.971L112.828 1.85788Z" fill="white"/>
<path d="M107.172 1.85788L106.464 1.15077V1.15078L107.172 1.85788ZM112.828 1.85788L113.536 1.15078V1.15077L112.828 1.85788ZM100.029 9.00001V10H100.444L100.737 9.70711L100.029 9.00001ZM119.971 9.00001L119.263 9.70711L119.556 10H119.971V9.00001ZM107.879 2.56499C109.05 1.39342 110.95 1.39342 112.121 2.56499L113.536 1.15077C111.583 -0.801845 108.417 -0.801845 106.464 1.15077L107.879 2.56499ZM100.737 9.70711L107.879 2.56499L106.464 1.15078L99.3223 8.2929L100.737 9.70711ZM4 10H100.029V8.00001H4V10ZM1 13C1 11.3432 2.34315 10 4 10V8.00001C1.23858 8.00001 -1 10.2386 -1 13H1ZM1 101V13H-1V101H1ZM4 104C2.34314 104 1 102.657 1 101H-1C-1 103.761 1.23857 106 4 106V104ZM216 104H4V106H216V104ZM219 101C219 102.657 217.657 104 216 104V106C218.761 106 221 103.761 221 101H219ZM219 13V101H221V13H219ZM216 10C217.657 10 219 11.3432 219 13H221C221 10.2386 218.761 8.00001 216 8.00001V10ZM119.971 10H216V8.00001H119.971V10ZM112.121 2.56499L119.263 9.70711L120.678 8.2929L113.536 1.15078L112.121 2.56499Z" fill="#E6ECF1" mask="url(#path-1-inside-1)"/>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -0,0 +1,7 @@
<svg width="127" height="89" viewBox="0 0 127 89" fill="none" xmlns="http://www.w3.org/2000/svg">
<mask id="path-1-inside-1" fill="white">
<path fill-rule="evenodd" clip-rule="evenodd" d="M65.8284 1.17157C64.2663 -0.390524 61.7337 -0.390524 60.1716 1.17157L53.3431 8.00001H4C1.79086 8.00001 0 9.79086 0 12V85C0 87.2091 1.79086 89 4 89H123C125.209 89 127 87.2092 127 85V12C127 9.79087 125.209 8.00001 123 8.00001H72.6569L65.8284 1.17157Z"/>
</mask>
<path fill-rule="evenodd" clip-rule="evenodd" d="M65.8284 1.17157C64.2663 -0.390524 61.7337 -0.390524 60.1716 1.17157L53.3431 8.00001H4C1.79086 8.00001 0 9.79086 0 12V85C0 87.2091 1.79086 89 4 89H123C125.209 89 127 87.2092 127 85V12C127 9.79087 125.209 8.00001 123 8.00001H72.6569L65.8284 1.17157Z" fill="white"/>
<path d="M60.1716 1.17157L60.8787 1.87868V1.87868L60.1716 1.17157ZM65.8284 1.17157L66.5355 0.464466V0.464465L65.8284 1.17157ZM53.3431 8.00001V9.00001H53.7574L54.0502 8.70711L53.3431 8.00001ZM72.6569 8.00001L71.9498 8.70711L72.2426 9.00001H72.6569V8.00001ZM60.8787 1.87868C62.0503 0.707108 63.9497 0.707105 65.1213 1.87868L66.5355 0.464465C64.5829 -1.48815 61.4171 -1.48816 59.4645 0.464467L60.8787 1.87868ZM54.0502 8.70711L60.8787 1.87868L59.4645 0.464466L52.636 7.2929L54.0502 8.70711ZM4 9.00001H53.3431V7.00001H4V9.00001ZM1 12C1 10.3431 2.34314 9.00001 4 9.00001V7.00001C1.23858 7.00001 -1 9.23858 -1 12H1ZM1 85V12H-1V85H1ZM4 88C2.34314 88 1 86.6569 1 85H-1C-1 87.7614 1.23857 90 4 90V88ZM123 88H4V90H123V88ZM126 85C126 86.6569 124.657 88 123 88V90C125.761 90 128 87.7614 128 85H126ZM126 12V85H128V12H126ZM123 9.00001C124.657 9.00001 126 10.3432 126 12H128C128 9.23858 125.761 7.00001 123 7.00001V9.00001ZM72.6569 9.00001H123V7.00001H72.6569V9.00001ZM65.1213 1.87868L71.9498 8.70711L73.364 7.2929L66.5355 0.464466L65.1213 1.87868Z" fill="#E6ECF1" mask="url(#path-1-inside-1)"/>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -0,0 +1,22 @@
<svg xmlns="http://www.w3.org/2000/svg" width="360" height="360">
<path fill="#90E1D9" fill-rule="evenodd" d="M120 300h60v60h-60v-60z"/>
<path fill="#5B33A2" fill-rule="evenodd" d="M120 360c-33.137 0-60-26.863-60-60h60v60z"/>
<path fill="#90E1D9" fill-rule="evenodd" d="M60 240h60v60H60v-60z"/>
<path fill="#5B33A2" fill-rule="evenodd" d="M120 240H60v-60c33.137 0 60 26.863 60 60z"/>
<path fill="#9987FC" fill-rule="evenodd" d="M60 240c-33.137 0-60-26.863-60-60h60v60z"/>
<path fill="#90E1D9" fill-rule="evenodd" d="M0 180c0-33.137 26.863-60 60-60v60H0z"/>
<path fill="#5B33A2" fill-rule="evenodd" d="M60 180v-60h60c0 33.137-26.863 60-60 60z"/>
<path fill="#61DC97" fill-rule="evenodd" d="M60 60h60v60H60V60z"/>
<path fill="#9987FC" fill-rule="evenodd" d="M60 60c0-33.137 26.863-60 60-60v60H60z"/>
<path fill="#5B33A2" fill-rule="evenodd" d="M120 0h60v60h-60V0z"/>
<path fill="#9987FC" fill-rule="evenodd" d="M300 300h60v60h-60v-60z"/>
<path fill="#5B33A2" fill-rule="evenodd" d="M300 360c-33.137 0-60-26.863-60-60h60v60z"/>
<path fill="#61DC97" fill-rule="evenodd" d="M240 240h60v60h-60v-60z"/>
<path fill="#5B33A2" fill-rule="evenodd" d="M300 240h-60v-60c33.137 0 60 26.863 60 60z"/>
<path fill="#9987FC" fill-rule="evenodd" d="M240 240c-33.137 0-60-26.863-60-60h60v60z"/>
<path fill="#5B33A2" fill-rule="evenodd" d="M180 180c0-33.137 26.863-60 60-60v60h-60z"/>
<path fill="#90E1D9" fill-rule="evenodd" d="M240 180v-60h60c0 33.137-26.863 60-60 60z"/>
<path fill="#61DC97" fill-rule="evenodd" d="M240 60h60v60h-60V60z"/>
<path fill="#5B33A2" fill-rule="evenodd" d="M240 60c0-33.137 26.863-60 60-60v60h-60z"/>
<path fill="#90E1D9" fill-rule="evenodd" d="M300 0h60v60h-60V0z"/>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@@ -0,0 +1,3 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M19.0917 10.7071C19.4823 10.3166 19.4822 9.68341 19.0917 9.29289L10.6064 0.807612C10.2159 0.417093 9.58277 0.417072 9.19223 0.807612C8.80169 1.19815 8.80171 1.83131 9.19223 2.22183L16.9704 10L9.19223 17.7782C8.80169 18.1687 8.80171 18.8019 9.19223 19.1924C9.58275 19.5829 10.2159 19.5829 10.6064 19.1924L19.0917 10.7071Z" fill="#9EAAB6"/>
</svg>

After

Width:  |  Height:  |  Size: 451 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

View File

@@ -0,0 +1,3 @@
<svg width="1" height="200" viewBox="0 0 1 200" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="1" height="200" fill="#E6ECF1"/>
</svg>

After

Width:  |  Height:  |  Size: 149 B

View File

@@ -0,0 +1,3 @@
<svg width="1" height="199" viewBox="0 0 1 199" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="1" height="199" fill="#E6ECF1"/>
</svg>

After

Width:  |  Height:  |  Size: 149 B

View File

@@ -0,0 +1,3 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M17.2923 17.2923C16.9011 17.6835 16.2669 17.6835 15.8757 17.2923L8.79285 10.2094L1.70996 17.2923C1.31878 17.6835 0.684559 17.6835 0.293382 17.2923C-0.0977942 16.9011 -0.0977941 16.2669 0.293383 15.8757L7.37627 8.79285L0.293383 1.70996C-0.0977941 1.31878 -0.0977942 0.684559 0.293382 0.293382C0.684559 -0.0977943 1.31878 -0.097794 1.70996 0.293383L8.79285 7.37627L15.8757 0.293407C16.2669 -0.09777 16.9011 -0.0977703 17.2923 0.293406C17.6835 0.684583 17.6835 1.31881 17.2923 1.70998L10.2094 8.79285L17.2923 15.8757C17.6835 16.2669 17.6835 16.9011 17.2923 17.2923Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 731 B

View File

@@ -0,0 +1,7 @@
<svg width="220" height="105" viewBox="0 0 220 105" fill="none" xmlns="http://www.w3.org/2000/svg">
<mask id="path-1-inside-1" fill="white">
<path fill-rule="evenodd" clip-rule="evenodd" d="M112.828 1.85788C111.266 0.295785 108.734 0.295786 107.172 1.85788L100.029 9.00001H4C1.79086 9.00001 0 10.7909 0 13V101C0 103.209 1.79086 105 4 105H216C218.209 105 220 103.209 220 101V13C220 10.7909 218.209 9.00001 216 9.00001H119.971L112.828 1.85788Z"/>
</mask>
<path fill-rule="evenodd" clip-rule="evenodd" d="M112.828 1.85788C111.266 0.295785 108.734 0.295786 107.172 1.85788L100.029 9.00001H4C1.79086 9.00001 0 10.7909 0 13V101C0 103.209 1.79086 105 4 105H216C218.209 105 220 103.209 220 101V13C220 10.7909 218.209 9.00001 216 9.00001H119.971L112.828 1.85788Z" fill="white"/>
<path d="M107.172 1.85788L106.464 1.15077V1.15078L107.172 1.85788ZM112.828 1.85788L113.536 1.15078V1.15077L112.828 1.85788ZM100.029 9.00001V10H100.444L100.737 9.70711L100.029 9.00001ZM119.971 9.00001L119.263 9.70711L119.556 10H119.971V9.00001ZM107.879 2.56499C109.05 1.39342 110.95 1.39342 112.121 2.56499L113.536 1.15077C111.583 -0.801845 108.417 -0.801845 106.464 1.15077L107.879 2.56499ZM100.737 9.70711L107.879 2.56499L106.464 1.15078L99.3223 8.2929L100.737 9.70711ZM4 10H100.029V8.00001H4V10ZM1 13C1 11.3432 2.34315 10 4 10V8.00001C1.23858 8.00001 -1 10.2386 -1 13H1ZM1 101V13H-1V101H1ZM4 104C2.34314 104 1 102.657 1 101H-1C-1 103.761 1.23857 106 4 106V104ZM216 104H4V106H216V104ZM219 101C219 102.657 217.657 104 216 104V106C218.761 106 221 103.761 221 101H219ZM219 13V101H221V13H219ZM216 10C217.657 10 219 11.3432 219 13H221C221 10.2386 218.761 8.00001 216 8.00001V10ZM119.971 10H216V8.00001H119.971V10ZM112.121 2.56499L119.263 9.70711L120.678 8.2929L113.536 1.15078L112.121 2.56499Z" fill="#E6ECF1" mask="url(#path-1-inside-1)"/>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -0,0 +1,7 @@
<svg width="127" height="89" viewBox="0 0 127 89" fill="none" xmlns="http://www.w3.org/2000/svg">
<mask id="path-1-inside-1" fill="white">
<path fill-rule="evenodd" clip-rule="evenodd" d="M65.8284 1.17157C64.2663 -0.390524 61.7337 -0.390524 60.1716 1.17157L53.3431 8.00001H4C1.79086 8.00001 0 9.79086 0 12V85C0 87.2091 1.79086 89 4 89H123C125.209 89 127 87.2092 127 85V12C127 9.79087 125.209 8.00001 123 8.00001H72.6569L65.8284 1.17157Z"/>
</mask>
<path fill-rule="evenodd" clip-rule="evenodd" d="M65.8284 1.17157C64.2663 -0.390524 61.7337 -0.390524 60.1716 1.17157L53.3431 8.00001H4C1.79086 8.00001 0 9.79086 0 12V85C0 87.2091 1.79086 89 4 89H123C125.209 89 127 87.2092 127 85V12C127 9.79087 125.209 8.00001 123 8.00001H72.6569L65.8284 1.17157Z" fill="white"/>
<path d="M60.1716 1.17157L60.8787 1.87868V1.87868L60.1716 1.17157ZM65.8284 1.17157L66.5355 0.464466V0.464465L65.8284 1.17157ZM53.3431 8.00001V9.00001H53.7574L54.0502 8.70711L53.3431 8.00001ZM72.6569 8.00001L71.9498 8.70711L72.2426 9.00001H72.6569V8.00001ZM60.8787 1.87868C62.0503 0.707108 63.9497 0.707105 65.1213 1.87868L66.5355 0.464465C64.5829 -1.48815 61.4171 -1.48816 59.4645 0.464467L60.8787 1.87868ZM54.0502 8.70711L60.8787 1.87868L59.4645 0.464466L52.636 7.2929L54.0502 8.70711ZM4 9.00001H53.3431V7.00001H4V9.00001ZM1 12C1 10.3431 2.34314 9.00001 4 9.00001V7.00001C1.23858 7.00001 -1 9.23858 -1 12H1ZM1 85V12H-1V85H1ZM4 88C2.34314 88 1 86.6569 1 85H-1C-1 87.7614 1.23857 90 4 90V88ZM123 88H4V90H123V88ZM126 85C126 86.6569 124.657 88 123 88V90C125.761 90 128 87.7614 128 85H126ZM126 12V85H128V12H126ZM123 9.00001C124.657 9.00001 126 10.3432 126 12H128C128 9.23858 125.761 7.00001 123 7.00001V9.00001ZM72.6569 9.00001H123V7.00001H72.6569V9.00001ZM65.1213 1.87868L71.9498 8.70711L73.364 7.2929L66.5355 0.464466L65.1213 1.87868Z" fill="#E6ECF1" mask="url(#path-1-inside-1)"/>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

View File

@@ -0,0 +1,3 @@
<svg width="60" height="60" viewBox="0 0 60 60" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="30" cy="30" r="29.5" fill="#F5F7F9" stroke="#E6ECF1"/>
</svg>

After

Width:  |  Height:  |  Size: 170 B

View File

@@ -0,0 +1,3 @@
<svg width="120" height="1" viewBox="0 0 120 1" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="120" height="1" fill="#E6ECF1"/>
</svg>

After

Width:  |  Height:  |  Size: 149 B

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 9.2 KiB

View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="34" height="8">
<path fill="#5c34a2" fill-rule="evenodd" d="M3.781 6.375h-.344v1.031a.333.333 0 0 1-.312.337v.007H.469v-.007c-.006.001-.01.007-.016.007a.336.336 0 0 1-.328-.344V1.219c0-.19.147-.344.328-.344.006 0 .01.006.016.007V.875h3.312c1.467 0 2.656 1.231 2.656 2.75s-1.189 2.75-2.656 2.75zM10.422.875c1.838 0 3.328 1.539 3.328 3.437 0 1.899-1.49 3.438-3.328 3.438-1.838 0-3.328-1.539-3.328-3.438 0-1.898 1.49-3.437 3.328-3.437zM21.369 7.532a.209.209 0 0 1-.205.212h-7.542c-.005.001-.008.006-.013.006-.112 0-.203-.098-.203-.219 0-.042.023-.076.042-.109l-.008-.017 3.739-6.363h.017c.021-.094.091-.17.188-.17.098 0 .167.076.189.17h.017l3.779 6.405-.021.032c.005.019.021.032.021.053zM33.562 7.75h-4.374a.313.313 0 0 1-.313-.313V1.188c0-.173.14-.313.313-.313h4.374c.173 0 .313.14.313.313v6.249c0 .173-.14.313-.313.313zM29.5 1.5v5.625h3.75V1.5H29.5zm-2.188 5.625a.313.313 0 1 1 0 .625h-4.374a.313.313 0 0 1-.313-.313V4.312c0-.172.14-.312.313-.312H27V1.5h-4.062a.313.313 0 1 1 0-.625h4.374c.173 0 .313.14.313.313v3.124c0 .173-.14.313-.313.313H23.25v2.5h4.062z"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="34" height="8">
<path fill="#FFF" fill-rule="evenodd" d="M3.781 6.375h-.344v1.031a.333.333 0 0 1-.312.337v.007H.469v-.007c-.006.001-.01.007-.016.007a.336.336 0 0 1-.328-.344V1.219c0-.19.147-.344.328-.344.006 0 .01.006.016.007V.875h3.312c1.467 0 2.656 1.231 2.656 2.75s-1.189 2.75-2.656 2.75zM10.422.875c1.838 0 3.328 1.539 3.328 3.437 0 1.899-1.49 3.438-3.328 3.438-1.838 0-3.328-1.539-3.328-3.438 0-1.898 1.49-3.437 3.328-3.437zM21.369 7.532a.209.209 0 0 1-.205.212h-7.542c-.005.001-.008.006-.013.006-.112 0-.203-.098-.203-.219 0-.042.023-.076.042-.109l-.008-.017 3.739-6.363h.017c.021-.094.091-.17.188-.17.098 0 .167.076.189.17h.017l3.779 6.405-.021.032c.005.019.021.032.021.053zM33.562 7.75h-4.374a.313.313 0 0 1-.313-.313V1.188c0-.173.14-.313.313-.313h4.374c.173 0 .313.14.313.313v6.249c0 .173-.14.313-.313.313zM29.5 1.5v5.625h3.75V1.5H29.5zm-2.188 5.625a.313.313 0 1 1 0 .625h-4.374a.313.313 0 0 1-.313-.313V4.312c0-.172.14-.312.313-.312H27V1.5h-4.062a.313.313 0 1 1 0-.625h4.374c.173 0 .313.14.313.313v3.124c0 .173-.14.313-.313.313H23.25v2.5h4.062z"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 5.8 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 9.2 KiB

View File

@@ -0,0 +1,23 @@
<svg xmlns="http://www.w3.org/2000/svg" width="59" height="59">
<path fill="#5C34A2" fill-rule="evenodd" d="M6 0h47a6 6 0 0 1 6 6v47a6 6 0 0 1-6 6H6a6 6 0 0 1-6-6V6a6 6 0 0 1 6-6z"/>
<path fill="#FFF" fill-rule="evenodd" d="M19 12h6v6h-6v-6z"/>
<path fill="#9987FC" fill-rule="evenodd" d="M13 18a6 6 0 0 1 6-6v6h-6z"/>
<path fill="#61DC97" fill-rule="evenodd" d="M13 18h6v6h-6v-6z"/>
<path fill="#FFF" fill-rule="evenodd" d="M13 30v-6h6a6 6 0 0 1-6 6z"/>
<path fill="#90E1D9" fill-rule="evenodd" d="M7 30a6 6 0 0 1 6-6v6H7z"/>
<path fill="#9987FC" fill-rule="evenodd" d="M13 36a6 6 0 0 1-6-6h6v6z"/>
<path fill="#90E1D9" fill-rule="evenodd" d="M19 36h-6v-6a6 6 0 0 1 6 6z"/>
<path fill="#FFF" fill-rule="evenodd" d="M13 36h6v6h-6v-6z"/>
<path fill="#61DC97" fill-rule="evenodd" d="M19 48a6 6 0 0 1-6-6h6v6z"/>
<path fill="#9987FC" fill-rule="evenodd" d="M19 42h6v6h-6v-6z"/>
<path fill="#FFF" fill-rule="evenodd" d="M35 42h6v6h-6v-6z"/>
<path fill="#9987FC" fill-rule="evenodd" d="M41 48v-6h6a6 6 0 0 1-6 6z"/>
<path fill="#61DC97" fill-rule="evenodd" d="M41 36h6v6h-6v-6z"/>
<path fill="#FFF" fill-rule="evenodd" d="M41 36a6 6 0 0 1 6-6v6h-6z"/>
<path fill="#90E1D9" fill-rule="evenodd" d="M47 36v-6h6a6 6 0 0 1-6 6z"/>
<path fill="#9987FC" fill-rule="evenodd" d="M53 30h-6v-6a6 6 0 0 1 6 6z"/>
<path fill="#90E1D9" fill-rule="evenodd" d="M47 30a6 6 0 0 1-6-6h6v6z"/>
<path fill="#FFF" fill-rule="evenodd" d="M41 18h6v6h-6v-6z"/>
<path fill="#61DC97" fill-rule="evenodd" d="M47 18h-6v-6a6 6 0 0 1 6 6z"/>
<path fill="#9987FC" fill-rule="evenodd" d="M35 12h6v6h-6v-6z"/>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -0,0 +1,23 @@
<svg xmlns="http://www.w3.org/2000/svg" width="59" height="59">
<path fill="#61DC97" fill-rule="evenodd" d="M6 0h47a6 6 0 0 1 6 6v47a6 6 0 0 1-6 6H6a6 6 0 0 1-6-6V6a6 6 0 0 1 6-6z"/>
<path fill="#FFF" fill-rule="evenodd" d="M19 12h6v6h-6v-6z"/>
<path fill="#9987FC" fill-rule="evenodd" d="M13 18a6 6 0 0 1 6-6v6h-6z"/>
<path fill="#5C34A2" fill-rule="evenodd" d="M13 18h6v6h-6v-6z"/>
<path fill="#FFF" fill-rule="evenodd" d="M13 30v-6h6a6 6 0 0 1-6 6z"/>
<path fill="#90E1D9" fill-rule="evenodd" d="M7 30a6 6 0 0 1 6-6v6H7z"/>
<path fill="#9987FC" fill-rule="evenodd" d="M13 36a6 6 0 0 1-6-6h6v6z"/>
<path fill="#90E1D9" fill-rule="evenodd" d="M19 36h-6v-6a6 6 0 0 1 6 6z"/>
<path fill="#FFF" fill-rule="evenodd" d="M13 36h6v6h-6v-6z"/>
<path fill="#5C34A2" fill-rule="evenodd" d="M19 48a6 6 0 0 1-6-6h6v6z"/>
<path fill="#9987FC" fill-rule="evenodd" d="M19 42h6v6h-6v-6z"/>
<path fill="#FFF" fill-rule="evenodd" d="M35 42h6v6h-6v-6z"/>
<path fill="#9987FC" fill-rule="evenodd" d="M41 48v-6h6a6 6 0 0 1-6 6z"/>
<path fill="#5C34A2" fill-rule="evenodd" d="M41 36h6v6h-6v-6z"/>
<path fill="#FFF" fill-rule="evenodd" d="M41 36a6 6 0 0 1 6-6v6h-6z"/>
<path fill="#90E1D9" fill-rule="evenodd" d="M47 36v-6h6a6 6 0 0 1-6 6z"/>
<path fill="#9987FC" fill-rule="evenodd" d="M53 30h-6v-6a6 6 0 0 1 6 6z"/>
<path fill="#90E1D9" fill-rule="evenodd" d="M47 30a6 6 0 0 1-6-6h6v6z"/>
<path fill="#FFF" fill-rule="evenodd" d="M41 18h6v6h-6v-6z"/>
<path fill="#5C34A2" fill-rule="evenodd" d="M47 18h-6v-6a6 6 0 0 1 6 6z"/>
<path fill="#9987FC" fill-rule="evenodd" d="M35 12h6v6h-6v-6z"/>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

Some files were not shown because too many files have changed in this diff Show More