diff --git a/.circleci/config.yml b/.circleci/config.yml index 26822ffa1b1..946d316eff3 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -12,7 +12,8 @@ reference: shell: /bin/bash --login -eo pipefail environment: TERM: dumb - JVM_OPTS: -Xmx3200m + _JAVA_OPTIONS: "-Xmx7g -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap" + GRADLE_OPTS: '-Dorg.gradle.daemon=false -Dorg.gradle.configureondemand=true -Dorg.gradle.jvmargs="-Xmx6g -XX:MaxPermSize=3g -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8"' defaults: &defaults working_directory: ~/app @@ -25,10 +26,10 @@ defaults: &defaults android-defaults: &android-defaults <<: *defaults docker: - - image: circleci/android:api-28-node8-alpha + - image: circleci/android:api-28-node environment: - _JAVA_OPTIONS: "-XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap" - GRADLE_OPTS: '-Dorg.gradle.daemon=false -Dorg.gradle.jvmargs="-Xmx1024m -XX:+HeapDumpOnOutOfMemoryError"' + _JAVA_OPTIONS: "-Xmx7g -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap" + GRADLE_OPTS: '-Dorg.gradle.daemon=false -Dorg.gradle.configureondemand=true -Dorg.gradle.jvmargs="-Xmx6g -XX:MaxPermSize=3g -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8"' e2e-defaults: &e2e-defaults <<: *defaults @@ -115,6 +116,10 @@ jobs: yarn build --scope @celo/walletkit yarn build --ignore @celo/protocol --ignore docs --ignore @celo/walletkit + - run: + name: Check licenses + command: yarn check-licenses + - persist_to_workspace: root: . paths: @@ -144,7 +149,6 @@ jobs: echo 'export ANDROID_SDK_ROOT="/usr/local/share/android-sdk"' >> $BASH_ENV echo 'export QEMU_AUDIO_DRV=none' >> $BASH_ENV export PATH=$PATH:/usr/local/share/android-sdk/platform-tools/ - export GRADLE_OPTS='-Dorg.gradle.daemon=true -Dorg.gradle.parallel=true -Dorg.gradle.jvmargs="-Xmx4096m -XX:+HeapDumpOnOutOfMemoryError"' - run: name: Install Android sdk command: | @@ -183,11 +187,12 @@ jobs: - run: nvm install v10.16.3 && nvm use v10.16.3 - run: name: install miscellaneous - command: HOMEBREW_NO_AUTO_UPDATE=1 brew install tree + command: | + HOMEBREW_NO_AUTO_UPDATE=1 brew install tree coreutils # Currently not used # - run: npm install --global react-native-kill-packager - run: - # need to run this because it's another OS than install_dependecies job + # need to run this because it's another OS than install_dependencies job name: yarn command: | # TODO, Make cache work and figure out why this doesn't work @@ -198,8 +203,9 @@ jobs: # echo "Cache found, just run post-script." # yarn postinstall # fi - yarn - yarn build + yarn || yarn + yarn build || yarn build + yarn run jetify - save_cache: key: yarn-v4-macos-{{ .Branch }}-{{ checksum "yarn.lock" }} paths: @@ -211,6 +217,9 @@ jobs: command: HOMEBREW_NO_AUTO_UPDATE=1 brew install pidcat watchman - restore_cache: key: yarn-v3-{{ arch }}-{{ .Branch }}-{{ checksum "packages/mobile/android/build.gradle" }}-{{ checksum "packages/mobile/android/settings.gradle" }}-{{ checksum "packages/mobile/android/app/build.gradle" }}-{{ checksum "packages/mobile/.env.test" }} + - run: + name: Make sure there's only one adb # This is probably a brew bug + command: cp /usr/local/share/android-sdk/platform-tools/adb /usr/local/bin/adb - run: name: Start emulator command: cd ~/src/packages/mobile && bash ./scripts/start_emulator.sh @@ -228,12 +237,26 @@ jobs: name: Sleep until Device connects command: cd ~/src/packages/mobile && bash ./scripts/wait_for_emulator_to_connect.sh # TODO - run: unlock device + - run: + name: Start pidcat logging + command: pidcat -t "GoLog" -t "Go" # React logs are on metro step since RN 61 + background: true - run: name: Run yarn dev command: cd ~/src/packages/mobile && ENVFILE=".env.test" yarn dev + - run: + name: Restart adb + command: adb kill-server && adb start-server - run: name: Run test itself - command: cd ~/src/packages/mobile && ENVFILE=".env.test" yarn test:detox + + command: | + cd ~/src/packages/mobile + # detox sometimes without releasing the terminal and thus making the CI timout + # 480s = 8 minutes + timeout 480 yarn test:detox || echo "failed, try again" + timeout 480 yarn test:detox || echo "detox failed, return 0 to prevent CI from failing" + # TODO errors are currently not reported, until we figure out why detox can't find functions https://github.com/wix/Detox/issues/1723 - run: cd ~/src - save_cache: key: yarn-v3-{{ arch }}-{{ .Branch }}-{{ checksum "packages/mobile/android/build.gradle" }}-{{ checksum "packages/mobile/android/settings.gradle" }}-{{ checksum "packages/mobile/android/app/build.gradle" }}-{{ checksum "packages/mobile/.env.test" }} @@ -264,22 +287,17 @@ jobs: yarn run lerna --ignore @celo/contractkit --ignore @celo/mobile --ignore @celo/protocol --ignore @celo/celotool --ignore @celo/walletkit --ignore @celo/celocli run test mobile-test-build-app: - working_directory: ~/app - docker: - - image: circleci/android:api-28 + <<: *android-defaults + resource_class: large steps: - attach_workspace: at: ~/app - - - run: - name: Set GRADLE_OPTS to speed up gradle - command: | - export GRADLE_OPTS='-Dorg.gradle.daemon=true -Dorg.gradle.parallel=true -Dorg.gradle.jvmargs="-Xmx4096m -XX:+HeapDumpOnOutOfMemoryError"' - run: name: Build Android app (debug version) command: | set -euo pipefail + yarn run jetify cd packages/mobile/android ENVFILE=.env.test ./gradlew assembleDebug cd - @@ -294,7 +312,7 @@ jobs: name: Ensure translations are not missing command: | cd packages/mobile - ./scripts/verify_locales.sh + yarn --cwd packages/mobile test:verify-locales - run: name: jest tests @@ -598,6 +616,24 @@ jobs: cd packages/celotool ./ci_test_attestations.sh checkout master + end-to-end-geth-validator-order-test: + <<: *e2e-defaults + resource_class: large + steps: + - attach_workspace: + at: ~/app + - run: + name: Check if the test should run + command: | + FILES_TO_CHECK="${PWD}/packages/celotool,${PWD}/packages/protocol,${PWD}/.circleci/config.yml" + ./scripts/ci_check_if_test_should_run_v2.sh ${FILES_TO_CHECK} + - run: + name: Run test + command: | + set -e + cd packages/celotool + ./ci_test_validator_order.sh checkout master + web: working_directory: ~/app docker: @@ -730,6 +766,10 @@ workflows: requires: - lint-checks - contractkit-test + - end-to-end-geth-validator-order-test: + requires: + - lint-checks + - contractkit-test npm-install-testing-cron-workflow: triggers: - schedule: diff --git a/.env b/.env index d7e98f29754..03d582d0a4e 100644 --- a/.env +++ b/.env @@ -12,20 +12,22 @@ CLUSTER_DOMAIN_NAME="celo-networks-dev" TESTNET_PROJECT_NAME="celo-testnet" BLOCKSCOUT_DOCKER_IMAGE_REPOSITORY="gcr.io/celo-testnet/blockscout" -BLOCKSCOUT_WEB_DOCKER_IMAGE_TAG="web-f6c3e0888d1d0ef72dc8bf870808702b7fd13730" -BLOCKSCOUT_INDEXER_DOCKER_IMAGE_TAG="indexer-f6c3e0888d1d0ef72dc8bf870808702b7fd13730" +BLOCKSCOUT_DOCKER_IMAGE_TAG="5fba4843b3e78b5ab75d01766214cb24c6a40649" BLOCKSCOUT_WEB_REPLICAS=3 BLOCKSCOUT_DB_SUFFIX= +ETHSTATS_DOCKER_IMAGE_REPOSITORY="gcr.io/celo-testnet/ethstats" +ETHSTATS_DOCKER_IMAGE_TAG="0ffe524c625ea59e4492dc92c2e638689c36e4b0" + GETH_NODE_DOCKER_IMAGE_REPOSITORY="us.gcr.io/celo-testnet/geth" # When upgrading change this to latest commit hash from the master of the geth repo # `geth $ git show | head -n 1` -GETH_NODE_DOCKER_IMAGE_TAG="7fbd6f3574f1c1c1e657c152fc63fb771adab3af" +GETH_NODE_DOCKER_IMAGE_TAG="ba213df07070433970d9b2cf75bae1d146cbfeda" GETH_BOOTNODE_DOCKER_IMAGE_REPOSITORY="gcr.io/celo-testnet/geth-all" # When upgrading change this to latest commit hash from the master of the geth repo # `geth $ git show | head -n 1` -GETH_BOOTNODE_DOCKER_IMAGE_TAG="7fbd6f3574f1c1c1e657c152fc63fb771adab3af" +GETH_BOOTNODE_DOCKER_IMAGE_TAG="ba213df07070433970d9b2cf75bae1d146cbfeda" CELOTOOL_DOCKER_IMAGE_REPOSITORY="gcr.io/celo-testnet/celo-monorepo" CELOTOOL_DOCKER_IMAGE_TAG="celotool-dfdc3e8b26e98aa294b27e2b5621c184488a10db" diff --git a/.env.integration b/.env.integration index 12da522873d..d34a1c869f4 100644 --- a/.env.integration +++ b/.env.integration @@ -11,8 +11,7 @@ CLUSTER_DOMAIN_NAME="celo-testnet" TESTNET_PROJECT_NAME="celo-testnet" BLOCKSCOUT_DOCKER_IMAGE_REPOSITORY="gcr.io/celo-testnet/blockscout" -BLOCKSCOUT_WEB_DOCKER_IMAGE_TAG="web-f6c3e0888d1d0ef72dc8bf870808702b7fd13730" -BLOCKSCOUT_INDEXER_DOCKER_IMAGE_TAG="indexer-f6c3e0888d1d0ef72dc8bf870808702b7fd13730" +BLOCKSCOUT_DOCKER_IMAGE_TAG="ad86714d629c01272e0651dec1fb6a968c3cec71" BLOCKSCOUT_WEB_REPLICAS=3 BLOCKSCOUT_DB_SUFFIX="25" BLOCKSCOUT_SUBNETWORK_NAME="Integration" diff --git a/README-dev.md b/README-dev.md index e8587875919..97d29247eae 100644 --- a/README-dev.md +++ b/README-dev.md @@ -4,10 +4,10 @@ Many packages depend on other packages within the monorepo. When this happens, follow these rules: - 1. All packages must use **master version** of sibling packages. - 2. Exception to (1) are packages that represent a GAE/firebase app which must use the last published version. - 3. To differentiate published vs unpublished version. Master version (in package.json) must end with suffix `-dev` and should not be published. - 4. If a developer want to publish a version; then after publishing it needs to set master version to next `-dev` version and change all package.json that require on it. +1. All packages must use **master version** of sibling packages. +2. Exception to (1) are packages that represent a GAE/firebase app which must use the last published version. +3. To differentiate published vs unpublished version. Master version (in package.json) must end with suffix `-dev` and should not be published. +4. If a developer want to publish a version; then after publishing it needs to set master version to next `-dev` version and change all package.json that require on it. To check which pakages need amending, you can run (in the root pkg): @@ -15,12 +15,12 @@ To check which pakages need amending, you can run (in the root pkg): A practical example: - * In any given moment, `contractkit/package.json#version` field **must** of the form `x.y.z-dev` - * If current version of contractkit is: `0.1.6-dev` and we want to publish a new version, we should: - * publish version `0.1.6` - * change `package.json#version` to `0.1.7-dev` - * change in other packages within monorepo that were using `0.1.6-dev` to `0.1.7-dev` - +- In any given moment, `contractkit/package.json#version` field **must** of the form `x.y.z-dev` +- If current version of contractkit is: `0.1.6-dev` and we want to publish a new version, we should: + - publish version `0.1.6` + - change `package.json#version` to `0.1.7-dev` + - change in other packages within monorepo that were using `0.1.6-dev` to `0.1.7-dev` + ## How to publish a new npm package First checkout the alfajores branch. diff --git a/SETUP.md b/SETUP.md index d4b9d9ca26f..d6cbef2dde0 100644 --- a/SETUP.md +++ b/SETUP.md @@ -101,7 +101,6 @@ Execute the following (and make sure the lines are in your `~/.bash_profile`): ```bash export ANDROID_HOME=/usr/local/share/android-sdk export ANDROID_NDK=/usr/local/share/android-ndk -# Optional to speedup java builds export GRADLE_OPTS='-Dorg.gradle.daemon=true -Dorg.gradle.parallel=true -Dorg.gradle.jvmargs="-Xmx4096m -XX:+HeapDumpOnOutOfMemoryError"' ``` @@ -205,7 +204,7 @@ Install the Android 28 system image and create an Android Virtual Device: ```bash sdkmanager "system-images;android-28;google_apis;x86" -avdmanager create avd --force --name Nexus_5X_API_28 --device "Nexus 5X" -k "system-images;android-28;google_apis;x86" --abi "google_apis/x86" +avdmanager create avd --force --name Nexus_5X_API_28_x86 --device "Nexus 5X" -k "system-images;android-28;google_apis;x86" --abi "google_apis/x86" ``` Execute the following and add it to your `~/.bash_profile`: diff --git a/dockerfiles/transaction-metrics-exporter/Dockerfile b/dockerfiles/transaction-metrics-exporter/Dockerfile index ae5b0ba9dba..1d9a063601e 100644 --- a/dockerfiles/transaction-metrics-exporter/Dockerfile +++ b/dockerfiles/transaction-metrics-exporter/Dockerfile @@ -18,8 +18,6 @@ COPY scripts/ scripts/ # Copy only pkg.json COPY packages/typescript/package.json packages/typescript/ COPY packages/utils/package.json packages/utils/ -COPY packages/protocol/package.json packages/protocol/ -COPY packages/contractkit/package.json packages/contractkit/ COPY packages/transaction-metrics-exporter/package.json packages/transaction-metrics-exporter/ RUN yarn install --network-timeout 100000 --frozen-lockfile && yarn cache clean @@ -27,8 +25,6 @@ RUN yarn install --network-timeout 100000 --frozen-lockfile && yarn cache clean # Copy the rest COPY packages/typescript packages/typescript/ COPY packages/utils packages/utils/ -COPY packages/protocol/ packages/protocol/ -COPY packages/contractkit packages/contractkit/ COPY packages/transaction-metrics-exporter packages/transaction-metrics-exporter/ # build all diff --git a/package.json b/package.json index eec07c2a6cf..a3220a74d25 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "reset-yarn": "yarn cache clean", "test": "yarn run lerna run test", "build": "yarn run lerna run build", + "check-licenses": "yarn licenses list --prod | grep '\\(─ GPL\\|─ (GPL-[1-9]\\.[0-9]\\+ OR GPL-[1-9]\\.[0-9]\\+)\\)' && echo 'Found GPL license(s). Use 'yarn licenses list --prod' to look up the offending package' || echo 'No GPL licenses found'", "report-coverage": "yarn run lerna run test-coverage", "test:watch": "node node_modules/jest/bin/jest.js --watch", "postinstall": "yarn run lerna run postinstall && patch-package && yarn keys:decrypt", @@ -35,9 +36,6 @@ "packages/*" ], "nohoist": [ - "@celo/verifier/react-native", - "@celo/mobile/react-native", - "@celo/react-components/react-native", "@celo/web/@timkendrick/monaco-editor", "@celo/web/@types/react-i18next", "@celo/web/next-i18next", @@ -45,7 +43,10 @@ ] }, "devDependencies": { - "@types/jest": "^24.0.17", + "@babel/core": "^7.6.2", + "@babel/runtime": "^7.6.2", + "@types/jest": "^24.0.18", + "babel-jest": "^24.9.0", "husky": "^3.0.0", "lerna": "^3.16.0", "patch-package": "^5.1.1", @@ -53,8 +54,10 @@ "pretty-quick": "^1.11.1", "solc": "0.5.8", "tslint": "^5.20.0", - "jest": "^24.8.0", - "ts-jest": "^24.0.2", + "jest": "^24.9.0", + "jest-junit": "^8.0.0", + "jest-snapshot": "^24.9.0", + "ts-jest": "^24.1.0", "typescript-tslint-plugin": "^0.5.4", "tsconfig-paths": "^3.8.0", "ts-node": "^8.3.0", diff --git a/packages/attestation-service/config/.env.development b/packages/attestation-service/config/.env.development index b3dbe6a82c5..a8b1540c59f 100644 --- a/packages/attestation-service/config/.env.development +++ b/packages/attestation-service/config/.env.development @@ -1,5 +1,5 @@ -DB_URL=sqlite://db/dev.db -CELO_PROVIDER=https://integration-infura.celo-testnet.org +DATABASE_URL=sqlite://db/dev.db +CELO_PROVIDER=https://integration-forno.celo-testnet.org ACCOUNT_ADDRESS=0xE6e53b5fc2e18F51781f14a3ce5E7FD468247a15 ATTESTATION_KEY=x APP_SIGNATURE=x diff --git a/packages/attestation-service/config/database.json b/packages/attestation-service/config/database.json index f15c454248a..76e76db046a 100644 --- a/packages/attestation-service/config/database.json +++ b/packages/attestation-service/config/database.json @@ -1,11 +1,11 @@ { "development": { - "use_env_variable": "DB_URL" + "use_env_variable": "DATABASE_URL" }, "staging": { - "use_env_variable": "DB_URL" + "use_env_variable": "DATABASE_URL" }, "production": { - "use_env_variable": "DB_URL" + "use_env_variable": "DATABASE_URL" } } diff --git a/packages/attestation-service/migrations/20191015211858-create-attestation.js b/packages/attestation-service/migrations/20191015211858-create-attestation.js new file mode 100644 index 00000000000..aca5106d69c --- /dev/null +++ b/packages/attestation-service/migrations/20191015211858-create-attestation.js @@ -0,0 +1,52 @@ +'use strict' +module.exports = { + up: async (queryInterface, Sequelize) => { + const transaction = await queryInterface.sequelize.transaction() + + try { + await queryInterface.createTable('Attestations', { + id: { + allowNull: false, + autoIncrement: true, + primaryKey: true, + type: Sequelize.INTEGER, + }, + account: { + allowNull: false, + type: Sequelize.STRING, + }, + phoneNumber: { + allowNull: false, + type: Sequelize.STRING, + }, + issuer: { + allowNull: false, + type: Sequelize.STRING, + }, + createdAt: { + allowNull: false, + type: Sequelize.DATE, + }, + updatedAt: { + allowNull: false, + type: Sequelize.DATE, + }, + }) + + await queryInterface.addIndex( + 'Attestations', + ['account', 'phoneNumber', 'issuer'], + { fields: ['account', 'phoneNumber', 'issuer'], unique: true }, + { transaction } + ) + + await transaction.commit() + } catch (error) { + await transaction.rollback() + throw error + } + }, + down: (queryInterface, Sequelize) => { + return queryInterface.dropTable('Attestations') + }, +} diff --git a/packages/attestation-service/package.json b/packages/attestation-service/package.json index 39cfc583511..99c3564d902 100644 --- a/packages/attestation-service/package.json +++ b/packages/attestation-service/package.json @@ -17,24 +17,29 @@ "clean": "tsc -b . --clean", "clean:all": "yarn clean && rm -rf lib", "prepublishOnly": "yarn build:gen && yarn build", - "start": "TS_NODE_FILES=true ts-node src/index.ts", + "start-ts": "TS_NODE_FILES=true ts-node src/index.ts", + "start": "node lib/index.js", "db:create:dev": "mkdir -p db && touch db/dev.db", "db:migrate": "sequelize db:migrate", + "db:migrate:undo": "sequelize db:migrate:undo", "db:migrate:dev": "sequelize db:migrate", "dev": "CONFIG=config/.env.development nodemon", "lint": "tslint -c tslint.json --project ." }, "dependencies": { - "@celo/contractkit": "0.1.1", + "@celo/contractkit": "0.1.6", "@celo/utils": "^0.1.0", "bignumber.js": "^7.2.0", "body-parser": "1.19.0", "debug": "^4.1.1", "dotenv": "8.0.0", "eth-lib": "^0.2.8", + "io-ts": "2.0.1", + "fp-ts":"2.1.1", "nexmo": "2.4.2", "web3": "1.0.0-beta.37", "express": "4.17.1", + "mysql2": "2.0.0-alpha1", "pg": "7.12.1", "pg-hstore": "2.3.3", "sequelize": "5.13.1", @@ -43,7 +48,6 @@ "yargs": "13.3.0" }, "devDependencies": { - "@celo/ganache-cli": "git+https://github.com/celo-org/ganache-cli.git#98ad2ba", "@celo/protocol": "1.0.0", "@types/dotenv": "4.0.3", "@types/debug": "^4.1.5", diff --git a/packages/attestation-service/src/attestation.ts b/packages/attestation-service/src/attestation.ts index 9790fef2de5..37411ea33f7 100644 --- a/packages/attestation-service/src/attestation.ts +++ b/packages/attestation-service/src/attestation.ts @@ -1,38 +1,124 @@ +import { AttestationState } from '@celo/contractkit/lib/wrappers/Attestations' import { attestToIdentifier, SignatureUtils } from '@celo/utils' +import { privateKeyToAddress } from '@celo/utils/lib/address' import { retryAsyncWithBackOff } from '@celo/utils/lib/async' import express from 'express' +import * as t from 'io-ts' +import { existingAttestationRequest, kit, persistAttestationRequest } from './db' +import { Address, AddressType, E164Number, E164PhoneNumberType } from './request' import { sendSms } from './sms' -function signAttestation(phoneNumber: string, account: string) { + +export const AttestationRequestType = t.type({ + phoneNumber: E164PhoneNumberType, + account: AddressType, + issuer: AddressType, +}) + +export type AttestationRequest = t.TypeOf + +function getAttestationKey() { if (process.env.ATTESTATION_KEY === undefined) { console.error('Did not specify ATTESTATION_KEY') throw new Error('Did not specify ATTESTATION_KEY') } - const signature = attestToIdentifier(phoneNumber, account, process.env.ATTESTATION_KEY) + return process.env.ATTESTATION_KEY +} + +async function validateAttestationRequest(request: AttestationRequest) { + // check if it exists in the database + if ( + (await existingAttestationRequest(request.phoneNumber, request.account, request.issuer)) !== + null + ) { + throw new Error('Attestation already sent') + } + const key = getAttestationKey() + const address = privateKeyToAddress(key) + + // TODO: Check with the new Accounts.sol + if (address.toLowerCase() !== request.issuer.toLowerCase()) { + throw new Error(`Mismatching issuer, I am ${address}`) + } + + const attestations = await kit.contracts.getAttestations() + const state = await attestations.getAttestationState( + request.phoneNumber, + request.account, + request.issuer + ) + + if (state.attestationState !== AttestationState.Incomplete) { + throw new Error('No incomplete attestation found') + } + + // TODO: Check expiration + return +} + +async function validateAttestation( + attestationRequest: AttestationRequest, + attestationCode: string +) { + const attestations = await kit.contracts.getAttestations() + const isValid = await attestations.validateAttestationCode( + attestationRequest.phoneNumber, + attestationRequest.account, + attestationRequest.issuer, + attestationCode + ) + if (!isValid) { + throw new Error('Valid attestation could not be provided') + } + return +} + +function signAttestation(phoneNumber: E164Number, account: Address) { + const signature = attestToIdentifier(phoneNumber, account, getAttestationKey()) return SignatureUtils.serializeSignature(signature) } function toBase64(str: string) { - return Buffer.from(str, 'hex').toString('base64') + return Buffer.from(str.slice(2), 'hex').toString('base64') } function createAttestationTextMessage(attestationCode: string) { return `<#> ${toBase64(attestationCode)} ${process.env.APP_SIGNATURE}` } -export async function handleAttestationRequest(req: express.Request, res: express.Response) { - // TODO: Should parse request appropriately - - // TODO: Should validate request here - // const attestations = await kit.contracts.getAttestations() - - // Produce attestation - const attestationCode = signAttestation(req.body.phoneNumber, req.body.account) - const textMessage = createAttestationTextMessage(attestationCode) +export async function handleAttestationRequest( + _req: express.Request, + res: express.Response, + attestationRequest: AttestationRequest +) { + let attestationCode + try { + await validateAttestationRequest(attestationRequest) + attestationCode = signAttestation(attestationRequest.phoneNumber, attestationRequest.account) + await validateAttestation(attestationRequest, attestationCode) + } catch (error) { + console.error(error) + res.status(422).json({ success: false, error: error.toString() }) + return + } - // Send the SMS - await retryAsyncWithBackOff(sendSms, 10, [req.body.phoneNumber, textMessage], 1000) + try { + const textMessage = createAttestationTextMessage(attestationCode) + await persistAttestationRequest( + attestationRequest.phoneNumber, + attestationRequest.account, + attestationRequest.issuer + ) + await retryAsyncWithBackOff(sendSms, 10, [attestationRequest.phoneNumber, textMessage], 1000) + } catch (error) { + console.error(error) + res.status(500).json({ + success: false, + error: 'Something went wrong while attempting to send SMS, try again later', + }) + return + } res.json({ success: true }) } diff --git a/packages/attestation-service/src/db.ts b/packages/attestation-service/src/db.ts index 2a2266bd372..5a75590d85c 100644 --- a/packages/attestation-service/src/db.ts +++ b/packages/attestation-service/src/db.ts @@ -1,15 +1,15 @@ import { ContractKit, newKit } from '@celo/contractkit' import { Sequelize } from 'sequelize' import { fetchEnv } from './env' +import Attestation, { AttestationStatic } from './models/attestation' export let sequelize: Sequelize | undefined export function initializeDB() { if (sequelize === undefined) { - sequelize = new Sequelize(fetchEnv('DB_URL')) + sequelize = new Sequelize(fetchEnv('DATABASE_URL')) return sequelize.authenticate() as Promise } - return Promise.resolve() } @@ -20,3 +20,29 @@ export function initializeKit() { kit = newKit(fetchEnv('CELO_PROVIDER')) } } + +let AttestationTable: AttestationStatic + +async function getAttestationTable() { + if (AttestationTable) { + return AttestationTable + } + AttestationTable = await Attestation(sequelize!) + return AttestationTable +} + +export async function existingAttestationRequest( + phoneNumber: string, + account: string, + issuer: string +): Promise { + return (await getAttestationTable()).findOne({ where: { phoneNumber, account, issuer } }) +} + +export async function persistAttestationRequest( + phoneNumber: string, + account: string, + issuer: string +) { + return (await getAttestationTable()).create({ phoneNumber, account, issuer }) +} diff --git a/packages/attestation-service/src/index.ts b/packages/attestation-service/src/index.ts index 6f55ba66bf7..8b7d059f1f1 100644 --- a/packages/attestation-service/src/index.ts +++ b/packages/attestation-service/src/index.ts @@ -1,7 +1,8 @@ import * as dotenv from 'dotenv' import express from 'express' -import { handleAttestationRequest } from './attestation' +import { AttestationRequestType, handleAttestationRequest } from './attestation' import { initializeDB, initializeKit } from './db' +import { createValidatedHandler } from './request' import { initializeSmsProviders } from './sms' async function init() { @@ -18,7 +19,10 @@ async function init() { const port = process.env.PORT || 3000 app.listen(port, () => console.log(`Server running on ${port}!`)) - app.post('/attestations', handleAttestationRequest) + app.post( + '/attestations', + createValidatedHandler(AttestationRequestType, handleAttestationRequest) + ) } init().catch((err) => { diff --git a/packages/attestation-service/src/models/attestation.ts b/packages/attestation-service/src/models/attestation.ts new file mode 100644 index 00000000000..a2b79e765b7 --- /dev/null +++ b/packages/attestation-service/src/models/attestation.ts @@ -0,0 +1,18 @@ +import { BuildOptions, DataTypes, Model, Sequelize } from 'sequelize' + +interface AttestationModel extends Model { + readonly id: number + account: string + phoneNumber: string + issuer: string +} + +export type AttestationStatic = typeof Model & + (new (values?: object, options?: BuildOptions) => AttestationModel) + +export default (sequelize: Sequelize) => + sequelize.define('Attestations', { + account: DataTypes.STRING, + phoneNumber: DataTypes.STRING, + issuer: DataTypes.STRING, + }) as AttestationStatic diff --git a/packages/attestation-service/src/request.ts b/packages/attestation-service/src/request.ts new file mode 100644 index 00000000000..dfd5ae13910 --- /dev/null +++ b/packages/attestation-service/src/request.ts @@ -0,0 +1,83 @@ +import { isE164NumberStrict } from '@celo/utils/lib/phoneNumbers' +import { isValidAddress } from '@celo/utils/lib/signatureUtils' +import express from 'express' +import { either, isLeft } from 'fp-ts/lib/Either' +import * as t from 'io-ts' + +export function createValidatedHandler( + requestType: t.Type, + handler: (req: express.Request, res: express.Response, parsedRequest: T) => Promise +) { + return async (req: express.Request, res: express.Response) => { + const parsedRequest = requestType.decode(req.body) + if (isLeft(parsedRequest)) { + res.status(422).json({ + success: false, + error: 'Error parsing invalid request', + errors: serializeErrors(parsedRequest.left), + }) + } else { + try { + await handler(req, res, parsedRequest.right) + } catch (error) { + console.error(error) + res.status(500).json({ success: false, error: 'Something went wrong' }) + } + } + } +} + +export const E164PhoneNumberType = new t.Type( + 'E164Number', + t.string.is, + (input, context) => + either.chain( + t.string.validate(input, context), + (stringValue) => + isE164NumberStrict(stringValue) + ? t.success(stringValue) + : t.failure(stringValue, context, 'is not a valid e164 number') + ), + String +) + +export const AddressType = new t.Type( + 'Address', + t.string.is, + (input, context) => + either.chain( + t.string.validate(input, context), + (stringValue) => + isValidAddress(stringValue) + ? t.success(stringValue) + : t.failure(stringValue, context, 'is not a valid address') + ), + String +) + +export type Address = t.TypeOf +export type E164Number = t.TypeOf + +function serializeErrors(errors: t.Errors) { + let serializedErrors: any = {} + errors.map((error) => { + const expectedType = error.context[error.context.length - 1].type + const path = error.context.map(({ key }) => key).join('.') + const value = + error.message || + `Expected value at path ${path} to be of type ${expectedType.name}, but received ${ + error.value + }` + + // Create recursive payload in case of nested properties + let payload: any = value + for (let index = error.context.length - 1; index > 0; index--) { + const innerError = payload + payload = {} + payload[error.context[index].key] = innerError + } + + serializedErrors = { ...serializedErrors, ...payload } + }) + return serializedErrors +} diff --git a/packages/blockchain-api/.env b/packages/blockchain-api/.env index 14b52cc6709..41d0732cc26 100644 --- a/packages/blockchain-api/.env +++ b/packages/blockchain-api/.env @@ -2,4 +2,4 @@ EXCHANGE_RATES_API=https://api.exchangeratesapi.io BLOCKSCOUT_API=https://integration-blockscout.celo-testnet.org/api FAUCET_ADDRESS=0x47e172F6CfB6c7D01C1574fa3E2Be7CC73269D95 VERIFICATION_REWARDS_ADDRESS=0xb4fdaf5f3cd313654aa357299ada901b1d2dd3b5 -WEB3_PROVIDER_URL=https://integration-infura.celo-testnet.org/ \ No newline at end of file +WEB3_PROVIDER_URL=https://integration-forno.celo-testnet.org/ \ No newline at end of file diff --git a/packages/blockchain-api/app.alfajores.yaml b/packages/blockchain-api/app.alfajores.yaml index 63560e388a2..9e896ec5069 100644 --- a/packages/blockchain-api/app.alfajores.yaml +++ b/packages/blockchain-api/app.alfajores.yaml @@ -8,4 +8,4 @@ env_variables: # TODO Pull addresses from the build artifacts of the network in protocol/build FAUCET_ADDRESS: "0xCEa3eF8e187490A9d85A1849D98412E5D27D1Bb3" VERIFICATION_REWARDS_ADDRESS: "0xb4fdaf5f3cd313654aa357299ada901b1d2dd3b5" - WEB3_PROVIDER_URL: "https://alfajores-infura.celo-testnet.org/" + WEB3_PROVIDER_URL: "https://alfajores-forno.celo-testnet.org/" diff --git a/packages/blockchain-api/app.alfajoresstaging.yaml b/packages/blockchain-api/app.alfajoresstaging.yaml index 10e9213b9f1..62f67433b6a 100644 --- a/packages/blockchain-api/app.alfajoresstaging.yaml +++ b/packages/blockchain-api/app.alfajoresstaging.yaml @@ -8,4 +8,4 @@ env_variables: # TODO Pull addresses from the build artifacts of the network in protocol/build FAUCET_ADDRESS: "0xF4314cb9046bECe6AA54bb9533155434d0c76909" VERIFICATION_REWARDS_ADDRESS: "0xb4fdaf5f3cd313654aa357299ada901b1d2dd3b5" - WEB3_PROVIDER_URL: "https://alfajoresstaging-infura.celo-testnet.org/" + WEB3_PROVIDER_URL: "https://alfajoresstaging-forno.celo-testnet.org/" diff --git a/packages/blockchain-api/app.integration.yaml b/packages/blockchain-api/app.integration.yaml index eeadf4a7e9e..ab6e9c19145 100644 --- a/packages/blockchain-api/app.integration.yaml +++ b/packages/blockchain-api/app.integration.yaml @@ -8,4 +8,4 @@ env_variables: # TODO Pull addresses from the build artifacts of the network in protocol/build FAUCET_ADDRESS: "0x47e172F6CfB6c7D01C1574fa3E2Be7CC73269D95" VERIFICATION_REWARDS_ADDRESS: "0xb4fdaf5f3cd313654aa357299ada901b1d2dd3b5" - WEB3_PROVIDER_URL: "https://integration-infura.celo-testnet.org/" \ No newline at end of file + WEB3_PROVIDER_URL: "https://integration-forno.celo-testnet.org/" diff --git a/packages/blockchain-api/app.pilot.yaml b/packages/blockchain-api/app.pilot.yaml index ba8dbdc3ec5..a3ad2aef526 100644 --- a/packages/blockchain-api/app.pilot.yaml +++ b/packages/blockchain-api/app.pilot.yaml @@ -8,4 +8,4 @@ env_variables: # TODO Pull addresses from the build artifacts of the network in protocol/build FAUCET_ADDRESS: "0x387bCb16Bfcd37AccEcF5c9eB2938E30d3aB8BF2" VERIFICATION_REWARDS_ADDRESS: "0xb4fdaf5f3cd313654aa357299ada901b1d2dd3b5" - WEB3_PROVIDER_URL: "https://pilot-infura.celo-testnet.org/" + WEB3_PROVIDER_URL: "https://pilot-forno.celo-testnet.org/" diff --git a/packages/blockchain-api/app.pilotstaging.yaml b/packages/blockchain-api/app.pilotstaging.yaml index 5a4e92e2cff..f83fbe3eeca 100644 --- a/packages/blockchain-api/app.pilotstaging.yaml +++ b/packages/blockchain-api/app.pilotstaging.yaml @@ -8,4 +8,4 @@ env_variables: # TODO Pull addresses from the build artifacts of the network in protocol/build FAUCET_ADDRESS: "0x545DEBe3030B570731EDab192640804AC8Cf65CA" VERIFICATION_REWARDS_ADDRESS: "0xb4fdaf5f3cd313654aa357299ada901b1d2dd3b5" - WEB3_PROVIDER_URL: "https://pilotstaging-infura.celo-testnet.org/" + WEB3_PROVIDER_URL: "https://pilotstaging-forno.celo-testnet.org/" diff --git a/packages/celotool/ci_test_governance.sh b/packages/celotool/ci_test_governance.sh index 39c193ea9f3..dd3f9ae867a 100755 --- a/packages/celotool/ci_test_governance.sh +++ b/packages/celotool/ci_test_governance.sh @@ -10,7 +10,7 @@ set -euo pipefail if [ "${1}" == "checkout" ]; then # Test master by default. - BRANCH_TO_TEST=${2:-"master"} + BRANCH_TO_TEST=${2:-"nambrot/accounts"} echo "Checking out geth at branch ${BRANCH_TO_TEST}..." ../../node_modules/.bin/mocha -r ts-node/register src/e2e-tests/governance_tests.ts --branch ${BRANCH_TO_TEST} elif [ "${1}" == "local" ]; then diff --git a/packages/celotool/ci_test_sync_with_network.sh b/packages/celotool/ci_test_sync_with_network.sh index ac7e8f6ac1d..99645d25d71 100755 --- a/packages/celotool/ci_test_sync_with_network.sh +++ b/packages/celotool/ci_test_sync_with_network.sh @@ -35,10 +35,8 @@ test_ultralight_sync () { echo "Testing ultralight sync with '${NETWORK_NAME}' network" # Run the sync in ultralight mode geth_tests/network_sync_test.sh ${NETWORK_NAME} ultralight - # Get the epoch size by sourcing this file - source ${CELO_MONOREPO_DIR}/.env.${NETWORK_NAME} # Verify what happened by reading the logs. - ${CELO_MONOREPO_DIR}/node_modules/.bin/mocha -r ts-node/register ${CELO_MONOREPO_DIR}/packages/celotool/src/e2e-tests/verify_ultralight_geth_logs.ts --gethlogfile ${GETH_LOG_FILE} --epoch ${EPOCH} + ${CELO_MONOREPO_DIR}/node_modules/.bin/mocha -r ts-node/register ${CELO_MONOREPO_DIR}/packages/celotool/src/e2e-tests/verify_ultralight_geth_logs.ts --network "${NETWORK_NAME}" --gethlogfile ${GETH_LOG_FILE} } # Some code in celotool requires this file to contain the MNEMONOIC. diff --git a/packages/celotool/ci_test_validator_order.sh b/packages/celotool/ci_test_validator_order.sh new file mode 100755 index 00000000000..085e01c786e --- /dev/null +++ b/packages/celotool/ci_test_validator_order.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash +set -euo pipefail + +# This test starts a standalone Geth node and runs transactions on it. + +# For testing a particular branch of Geth repo (usually, on Circle CI) +# Usage: ci_test_validator_order.sh checkout +# For testing the local Geth dir (usually, for manual testing) +# Usage: ci_test_validator_order.sh local + +if [ "${1}" == "checkout" ]; then + # Test master by default. + BRANCH_TO_TEST=${2:-"master"} + echo "Checking out geth at branch ${BRANCH_TO_TEST}..." + ../../node_modules/.bin/mocha -r ts-node/register src/e2e-tests/validator_order_tests.ts --branch ${BRANCH_TO_TEST} +elif [ "${1}" == "local" ]; then + export GETH_DIR="${2}" + echo "Testing using local geth dir ${GETH_DIR}..." + ../../node_modules/.bin/mocha -r ts-node/register src/e2e-tests/validator_order_tests.ts --localgeth ${GETH_DIR} +fi diff --git a/packages/celotool/package.json b/packages/celotool/package.json index fd4d2039aba..4d0b5fb07b1 100644 --- a/packages/celotool/package.json +++ b/packages/celotool/package.json @@ -18,7 +18,7 @@ "bignumber.js": "^7.2.0", "bip32": "^1.0.2", "bip39": "^2.5.0", - "bls12377js": "https://github.com/celo-org/bls12377js#4f596cabb659c4f8969ae4b617f185f2bc74cbbb", + "bls12377js": "https://github.com/celo-org/bls12377js#cada1105f4a5e4c2ddd239c1874df3bf33144a10", "dotenv": "6.1.0", "ecurve": "^1.0.6", "js-yaml": "^3.13.1", diff --git a/packages/celotool/src/cmds/account/verify.ts b/packages/celotool/src/cmds/account/verify.ts index 63b16bc48d4..fbb371a80a0 100644 --- a/packages/celotool/src/cmds/account/verify.ts +++ b/packages/celotool/src/cmds/account/verify.ts @@ -1,8 +1,11 @@ import { AccountArgv } from '@celo/celotool/src/cmds/account' import { portForwardAnd } from '@celo/celotool/src/lib/port_forward' import { newKit } from '@celo/contractkit' -import { AttestationsWrapper } from '@celo/contractkit/lib/wrappers/Attestations' -import { ActionableAttestation, decodeAttestationCode } from '@celo/walletkit' +import { + ActionableAttestation, + AttestationsWrapper, +} from '@celo/contractkit/lib/wrappers/Attestations' +import { base64ToHex } from '@celo/utils/lib/attestations' import prompts from 'prompts' import { switchToClusterFromEnv } from 'src/lib/cluster' import * as yargs from 'yargs' @@ -49,6 +52,7 @@ async function verifyCmd(argv: VerifyArgv) { kit.defaultAccount = account const attestations = await kit.contracts.getAttestations() + const accounts = await kit.contracts.getAccounts() await printCurrentCompletedAttestations(attestations, argv.phone, account) let attestationsToComplete = await attestations.getActionableAttestations(argv.phone, account) @@ -59,15 +63,16 @@ async function verifyCmd(argv: VerifyArgv) { await requestMoreAttestations( attestations, argv.phone, - argv.num - attestationsToComplete.length + argv.num - attestationsToComplete.length, + account ) } // Set the wallet address if not already appropriate - const currentWalletAddress = await attestations.getWalletAddress(account) + const currentWalletAddress = await accounts.getWalletAddress(account) if (currentWalletAddress !== account) { - const setWalletAddressTx = await attestations.setWalletAddress(account) + const setWalletAddressTx = await accounts.setWalletAddress(account) const result = await setWalletAddressTx.send() await result.waitReceipt() } @@ -97,7 +102,8 @@ export async function printCurrentCompletedAttestations( async function requestMoreAttestations( attestations: AttestationsWrapper, phoneNumber: string, - attestationsRequested: number + attestationsRequested: number, + account: string ) { await attestations .approveAttestationFee(attestationsRequested) @@ -105,6 +111,8 @@ async function requestMoreAttestations( await attestations .request(phoneNumber, attestationsRequested) .then((txo) => txo.sendAndWaitForReceipt()) + await attestations.waitForSelectingIssuers(phoneNumber, account) + await attestations.selectIssuers(phoneNumber).then((txo) => txo.sendAndWaitForReceipt()) } async function revealAttestations( @@ -128,7 +136,7 @@ async function verifyCode( account: string, attestationsToComplete: ActionableAttestation[] ) { - const code = decodeAttestationCode(base64Code) + const code = base64ToHex(base64Code) const matchingIssuer = attestations.findMatchingIssuer( phoneNumber, account, @@ -147,7 +155,7 @@ async function verifyCode( matchingIssuer, code ) - if (isValidRequest === NULL_ADDRESS) { + if (!isValidRequest) { console.warn('Code was not valid') return } diff --git a/packages/celotool/src/cmds/deploy/initial/contracts.ts b/packages/celotool/src/cmds/deploy/initial/contracts.ts index 161ae6604d1..ddcfe777648 100644 --- a/packages/celotool/src/cmds/deploy/initial/contracts.ts +++ b/packages/celotool/src/cmds/deploy/initial/contracts.ts @@ -95,8 +95,8 @@ export async function registerMetadata(testnet: string, privateKey: string, inde kit.addAccount(privateKey) kit.defaultAccount = address - const attestations = await kit.contracts.getAttestations() - return attestations + const accounts = await kit.contracts.getAccounts() + return accounts .setMetadataURL(metadataURLForCLabsValidator(testnet, address)) .sendAndWaitForReceipt() } @@ -113,7 +113,12 @@ export const handler = async (argv: InitialArgv) => { validatorKeys, }, stableToken: { - initialAccounts: getAddressesFor(AccountType.FAUCET, mnemonic, 2), + initialBalances: { + addresses: getAddressesFor(AccountType.FAUCET, mnemonic, 2), + values: getAddressesFor(AccountType.FAUCET, mnemonic, 2).map( + () => '60000000000000000000000' + ), // 60k Celo Dollars + }, }, }) diff --git a/packages/celotool/src/e2e-tests/attestations_tests.ts b/packages/celotool/src/e2e-tests/attestations_tests.ts index f9bcf5f07d9..b557f2e83cc 100644 --- a/packages/celotool/src/e2e-tests/attestations_tests.ts +++ b/packages/celotool/src/e2e-tests/attestations_tests.ts @@ -49,11 +49,16 @@ describe('governance tests', () => { it('requests an attestation', async function(this: any) { this.timeout(10000) + const approve = await Attestations.approveAttestationFee(2) await approve.sendAndWaitForReceipt() const request = await Attestations.request(phoneNumber, 2) await request.sendAndWaitForReceipt() + await Attestations.waitForSelectingIssuers(phoneNumber, validatorAddress) + const selectIssuers = await Attestations.selectIssuers(phoneNumber) + await selectIssuers.sendAndWaitForReceipt() + const stats = await Attestations.getAttestationStat(phoneNumber, validatorAddress) assert.equal(stats.total, 2) const actionable = await Attestations.getActionableAttestations(phoneNumber, validatorAddress) diff --git a/packages/celotool/src/e2e-tests/exit_test.ts b/packages/celotool/src/e2e-tests/exit_test.ts index b0f06387942..21a1a0fd024 100644 --- a/packages/celotool/src/e2e-tests/exit_test.ts +++ b/packages/celotool/src/e2e-tests/exit_test.ts @@ -30,7 +30,7 @@ describe('exit tests', function(this: any) { this.timeout(0) const gethConfig: GethTestConfig = { - migrateTo: 15, + migrateTo: 17, instances: [ { name: 'validator', validating: true, syncmode: 'full', port: 30303, rpcport: 8545 }, ], diff --git a/packages/celotool/src/e2e-tests/governance_tests.ts b/packages/celotool/src/e2e-tests/governance_tests.ts index a4a97f87cb0..71d318a64b5 100644 --- a/packages/celotool/src/e2e-tests/governance_tests.ts +++ b/packages/celotool/src/e2e-tests/governance_tests.ts @@ -1,186 +1,10 @@ +import { ContractKit, newKitFromWeb3 } from '@celo/contractkit' +import { AccountsWrapper } from '@celo/contractkit/lib/wrappers/Accounts' +import { fromFixed, toFixed } from '@celo/utils/lib/fixidity' import BigNumber from 'bignumber.js' import { assert } from 'chai' import Web3 from 'web3' -import { strip0x } from '../lib/utils' -import { - assertRevert, - erc20Abi, - getContext, - getContractAddress, - getEnode, - importGenesis, - initAndStartGeth, - sleep, -} from './utils' - -// TODO(asa): Use the contract kit here instead -const electionAbi = [ - { - constant: true, - inputs: [ - { - name: 'index', - type: 'uint256', - }, - ], - name: 'validatorAddressFromCurrentSet', - outputs: [ - { - name: '', - type: 'address', - }, - ], - payable: false, - stateMutability: 'view', - type: 'function', - }, - { - constant: true, - inputs: [], - name: 'numberValidatorsInCurrentSet', - outputs: [ - { - name: '', - type: 'uint256', - }, - ], - payable: false, - stateMutability: 'view', - type: 'function', - }, -] - -const validatorsAbi = [ - { - constant: true, - inputs: [], - name: 'getRegisteredValidatorGroups', - outputs: [ - { - name: '', - type: 'address[]', - }, - ], - payable: false, - stateMutability: 'view', - type: 'function', - }, - { - constant: true, - inputs: [ - { - name: 'account', - type: 'address', - }, - ], - name: 'getValidatorGroup', - outputs: [ - { - name: '', - type: 'string', - }, - { - name: '', - type: 'string', - }, - { - name: '', - type: 'address[]', - }, - ], - payable: false, - stateMutability: 'view', - type: 'function', - }, - { - constant: false, - inputs: [ - { - name: 'validator', - type: 'address', - }, - ], - name: 'addMember', - outputs: [ - { - name: '', - type: 'bool', - }, - ], - payable: false, - stateMutability: 'nonpayable', - type: 'function', - }, - { - constant: false, - inputs: [ - { - name: 'validator', - type: 'address', - }, - ], - name: 'removeMember', - outputs: [ - { - name: '', - type: 'bool', - }, - ], - payable: false, - stateMutability: 'nonpayable', - type: 'function', - }, - { - constant: true, - inputs: [ - { - name: 'index', - type: 'uint256', - }, - ], - name: 'validatorAddressFromCurrentSet', - outputs: [ - { - name: '', - type: 'address', - }, - ], - payable: false, - stateMutability: 'view', - type: 'function', - }, - { - constant: true, - inputs: [], - name: 'numberValidatorsInCurrentSet', - outputs: [ - { - name: '', - type: 'uint256', - }, - ], - payable: false, - stateMutability: 'view', - type: 'function', - }, - { - anonymous: false, - inputs: [ - { - indexed: true, - name: 'previousOwner', - type: 'address', - }, - { - indexed: true, - name: 'newOwner', - type: 'address', - }, - ], - name: 'OwnershipTransferred', - type: 'event', - }, -] +import { getContext, getEnode, importGenesis, initAndStartGeth, sleep } from './utils' describe('governance tests', () => { const gethConfig = { @@ -199,6 +23,9 @@ describe('governance tests', () => { let election: any let validators: any let goldToken: any + let registry: any + let accounts: AccountsWrapper + let kit: ContractKit before(async function(this: any) { this.timeout(0) @@ -210,9 +37,12 @@ describe('governance tests', () => { const restart = async () => { await context.hooks.restart() web3 = new Web3('http://localhost:8545') - goldToken = new web3.eth.Contract(erc20Abi, await getContractAddress('GoldTokenProxy')) - validators = new web3.eth.Contract(validatorsAbi, await getContractAddress('ValidatorsProxy')) - election = new web3.eth.Contract(electionAbi, await getContractAddress('ElectionProxy')) + kit = newKitFromWeb3(web3) + goldToken = await kit._web3Contracts.getGoldToken() + validators = await kit._web3Contracts.getValidators() + registry = await kit._web3Contracts.getRegistry() + election = await kit._web3Contracts.getElection() + accounts = await kit.contracts.getAccounts() } const unlockAccount = async (address: string, theWeb3: any) => { @@ -220,16 +50,26 @@ describe('governance tests', () => { await theWeb3.eth.personal.unlockAccount(address, '', 1000) } - const getValidatorGroupMembers = async () => { - const [groupAddress] = await validators.methods.getRegisteredValidatorGroups().call() - const groupInfo = await validators.methods.getValidatorGroup(groupAddress).call() - return groupInfo[2] + const getValidatorGroupMembers = async (blockNumber?: number) => { + if (blockNumber) { + const [groupAddress] = await validators.methods + .getRegisteredValidatorGroups() + .call({}, blockNumber) + const groupInfo = await validators.methods + .getValidatorGroup(groupAddress) + .call({}, blockNumber) + return groupInfo[0] + } else { + const [groupAddress] = await validators.methods.getRegisteredValidatorGroups().call() + const groupInfo = await validators.methods.getValidatorGroup(groupAddress).call() + return groupInfo[0] + } } const getValidatorGroupKeys = async () => { const [groupAddress] = await validators.methods.getRegisteredValidatorGroups().call() - const groupInfo = await validators.methods.getValidatorGroup(groupAddress).call() - const encryptedKeystore64 = groupInfo[0].split(' ')[1] + const name = await accounts.getName(groupAddress) + const encryptedKeystore64 = name.split(' ')[1] const encryptedKeystore = JSON.parse(Buffer.from(encryptedKeystore64, 'base64').toString()) // The validator group ID is the validator group keystore encrypted with validator 0's // private key. @@ -239,6 +79,17 @@ describe('governance tests', () => { return [groupAddress, decryptedKeystore.privateKey] } + const activate = async (account: string, txOptions: any = {}) => { + await unlockAccount(account, web3) + const [group] = await validators.methods.getRegisteredValidatorGroups().call() + const tx = election.methods.activate(group) + let gas = txOptions.gas + if (!gas) { + gas = await tx.estimateGas({ ...txOptions }) + } + return tx.send({ from: account, ...txOptions, gas }) + } + const removeMember = async ( groupWeb3: any, group: string, @@ -264,262 +115,341 @@ describe('governance tests', () => { return tx.send({ from: group, ...txOptions, gas }) } - describe('Election.numberValidatorsInCurrentSet()', () => { - before(async function() { - this.timeout(0) + const isLastBlockOfEpoch = (blockNumber: number, epochSize: number) => { + return blockNumber % epochSize === 0 + } + + describe('when the validator set is changing', () => { + let epoch: number + const blockNumbers: number[] = [] + let allValidators: string[] + before(async function(this: any) { + this.timeout(0) // Disable test timeout await restart() - }) + const [groupAddress, groupPrivateKey] = await getValidatorGroupKeys() - it('should return the validator set size', async () => { - const numberValidators = await election.methods.numberValidatorsInCurrentSet().call() - assert.equal(numberValidators, 5) - }) + const groupInstance = { + name: 'validatorGroup', + validating: false, + syncmode: 'full', + port: 30325, + wsport: 8567, + privateKey: groupPrivateKey.slice(2), + peers: [await getEnode(8545)], + } + await initAndStartGeth(context.hooks.gethBinaryPath, groupInstance) + allValidators = await getValidatorGroupMembers() + assert.equal(allValidators.length, 5) + epoch = new BigNumber(await validators.methods.getEpochSize().call()).toNumber() + assert.equal(epoch, 10) + + // Give the node time to sync, and time for an epoch transition so we can activate our vote. + await sleep(20) + await activate(allValidators[0]) + const groupWeb3 = new Web3('ws://localhost:8567') + const groupKit = newKitFromWeb3(groupWeb3) + validators = await groupKit._web3Contracts.getValidators() + const membersToSwap = [allValidators[0], allValidators[1]] + let includedMemberIndex = 1 + await removeMember(groupWeb3, groupAddress, membersToSwap[0]) - describe('after the validator set changes', () => { - before(async function() { - this.timeout(0) - await restart() - const [groupAddress, groupPrivateKey] = await getValidatorGroupKeys() - const epoch = 10 - - const groupInstance = { - name: 'validatorGroup', - validating: false, - syncmode: 'full', - port: 30325, - wsport: 8567, - privateKey: groupPrivateKey.slice(2), - peers: [await getEnode(8545)], + const changeValidatorSet = async (header: any) => { + blockNumbers.push(header.number) + // At the start of epoch N, swap members so the validator set is different for epoch N + 1. + if (header.number % epoch === 1) { + const memberToRemove = membersToSwap[includedMemberIndex] + const memberToAdd = membersToSwap[(includedMemberIndex + 1) % 2] + await removeMember(groupWeb3, groupAddress, memberToRemove) + await addMember(groupWeb3, groupAddress, memberToAdd) + includedMemberIndex = (includedMemberIndex + 1) % 2 + const newMembers = await getValidatorGroupMembers() + assert.include(newMembers, memberToAdd) + assert.notInclude(newMembers, memberToRemove) } - await initAndStartGeth(context.hooks.gethBinaryPath, groupInstance) - const groupWeb3 = new Web3('ws://localhost:8567') - election = new web3.eth.Contract(electionAbi, await getContractAddress('ElectionProxy')) - validators = new groupWeb3.eth.Contract( - validatorsAbi, - await getContractAddress('ValidatorsProxy') + } + + const subscription = await groupWeb3.eth.subscribe('newBlockHeaders') + subscription.on('data', changeValidatorSet) + // Wait for a few epochs while changing the validator set. + await sleep(epoch * 4) + ;(subscription as any).unsubscribe() + // Wait for the current epoch to complete. + await sleep(epoch) + }) + + const getValidatorSetAtBlock = async (blockNumber: number) => { + const validatorSetSize = await election.methods + .numberValidatorsInCurrentSet() + .call({}, blockNumber) + const validatorSet = [] + for (let i = 0; i < validatorSetSize; i++) { + validatorSet.push( + await election.methods.validatorAddressFromCurrentSet(i).call({}, blockNumber) ) - // Give the node time to sync. - await sleep(15) - const members = await getValidatorGroupMembers() - await removeMember(groupWeb3, groupAddress, members[0]) - await sleep(epoch * 2) - }) + } + return validatorSet + } - it('should return the reduced validator set size', async () => { - const numberValidators = await election.methods.numberValidatorsInCurrentSet().call() + const getLastEpochBlock = (blockNumber: number) => { + const epochNumber = Math.floor((blockNumber - 1) / epoch) + return epochNumber * epoch + } - assert.equal(numberValidators, 4) - }) + it('should always return a validator set size equal to the number of group members at the end of the last epoch', async () => { + for (const blockNumber of blockNumbers) { + const lastEpochBlock = getLastEpochBlock(blockNumber) + const validatorSetSize = await election.methods + .numberValidatorsInCurrentSet() + .call({}, blockNumber) + const groupMembership = await getValidatorGroupMembers(lastEpochBlock) + assert.equal(validatorSetSize, groupMembership.length) + } }) - }) - describe('Election.validatorAddressFromCurrentSet()', () => { - before(async function() { - this.timeout(0) - await restart() + it('should always return a validator set equal to the group members at the end of the last epoch', async () => { + for (const blockNumber of blockNumbers) { + const lastEpochBlock = getLastEpochBlock(blockNumber) + const groupMembership = await getValidatorGroupMembers(lastEpochBlock) + const validatorSet = await getValidatorSetAtBlock(blockNumber) + assert.sameMembers(groupMembership, validatorSet) + } }) - it('should return the first validator', async () => { - const resultAddress = await election.methods.validatorAddressFromCurrentSet(0).call() - - assert.equal(strip0x(resultAddress), context.validators[0].address) + it('should only have created blocks whose miner was in the current validator set', async () => { + for (const blockNumber of blockNumbers) { + const validatorSet = await getValidatorSetAtBlock(blockNumber) + const block = await web3.eth.getBlock(blockNumber) + assert.include(validatorSet.map((x) => x.toLowerCase()), block.miner.toLowerCase()) + } }) - it('should return the third validator', async () => { - const resultAddress = await election.methods.validatorAddressFromCurrentSet(2).call() + it('should update the validator scores at the end of each epoch', async () => { + const adjustmentSpeed = fromFixed( + new BigNumber((await validators.methods.getValidatorScoreParameters().call())[1]) + ) + const uptime = 1 - assert.equal(strip0x(resultAddress), context.validators[2].address) - }) + const assertScoreUnchanged = async (validator: string, blockNumber: number) => { + const score = new BigNumber( + (await validators.methods.getValidator(validator).call({}, blockNumber))[3] + ) + const previousScore = new BigNumber( + (await validators.methods.getValidator(validator).call({}, blockNumber - 1))[3] + ) + assert.isNotNaN(score) + assert.isNotNaN(previousScore) + assert.equal(score.toFixed(), previousScore.toFixed()) + } - it('should return the fifth validator', async () => { - const resultAddress = await election.methods.validatorAddressFromCurrentSet(4).call() + const assertScoreChanged = async (validator: string, blockNumber: number) => { + const score = new BigNumber( + (await validators.methods.getValidator(validator).call({}, blockNumber))[3] + ) + const previousScore = new BigNumber( + (await validators.methods.getValidator(validator).call({}, blockNumber - 1))[3] + ) + const expectedScore = adjustmentSpeed + .times(uptime) + .plus(new BigNumber(1).minus(adjustmentSpeed).times(fromFixed(previousScore))) + assert.isNotNaN(score) + assert.isNotNaN(previousScore) + assert.equal(score.toFixed(), toFixed(expectedScore).toFixed()) + } - assert.equal(strip0x(resultAddress), context.validators[4].address) - }) + for (const blockNumber of blockNumbers) { + let expectUnchangedScores: string[] + let expectChangedScores: string[] + if (isLastBlockOfEpoch(blockNumber, epoch)) { + expectChangedScores = await getValidatorSetAtBlock(blockNumber) + expectUnchangedScores = allValidators.filter((x) => !expectChangedScores.includes(x)) + } else { + expectUnchangedScores = allValidators + expectChangedScores = [] + } - it('should revert when asked for an out of bounds validator', async function(this: any) { - this.timeout(0) // Disable test timeout - await assertRevert( - election.methods.validatorAddressFromCurrentSet(5).send({ - from: `0x${context.validators[0].address}`, - }) - ) - }) + for (const validator of expectUnchangedScores) { + await assertScoreUnchanged(validator, blockNumber) + } - describe('after the validator set changes', () => { - before(async function() { - this.timeout(0) - await restart() - const [groupAddress, groupPrivateKey] = await getValidatorGroupKeys() - const epoch = 10 - - const groupInstance = { - name: 'validatorGroup', - validating: false, - syncmode: 'full', - port: 30325, - wsport: 8567, - privateKey: groupPrivateKey.slice(2), - peers: [await getEnode(8545)], + for (const validator of expectChangedScores) { + await assertScoreChanged(validator, blockNumber) } - await initAndStartGeth(context.hooks.gethBinaryPath, groupInstance) - const groupWeb3 = new Web3('ws://localhost:8567') - validators = new groupWeb3.eth.Contract( - validatorsAbi, - await getContractAddress('ValidatorsProxy') + } + }) + + it('should distribute epoch payments at the end of each epoch', async () => { + const stableToken = await kit._web3Contracts.getStableToken() + const commission = 0.1 + const validatorEpochPayment = new BigNumber( + await validators.methods.validatorEpochPayment().call() + ) + const [group] = await validators.methods.getRegisteredValidatorGroups().call() + + const assertBalanceChanged = async ( + validator: string, + blockNumber: number, + expected: BigNumber + ) => { + const currentBalance = new BigNumber( + await stableToken.methods.balanceOf(validator).call({}, blockNumber) ) - // Give the node time to sync. - await sleep(15) - const members = await getValidatorGroupMembers() - await removeMember(groupWeb3, groupAddress, members[0]) - await sleep(epoch * 2) - - validators = new web3.eth.Contract( - validatorsAbi, - await getContractAddress('ValidatorsProxy') + const previousBalance = new BigNumber( + await stableToken.methods.balanceOf(validator).call({}, blockNumber - 1) ) - }) + assert.isNotNaN(currentBalance) + assert.isNotNaN(previousBalance) + assert.equal(expected.toFixed(), currentBalance.minus(previousBalance).toFixed()) + } - it('should return the second validator in the first place', async () => { - const resultAddress = await election.methods.validatorAddressFromCurrentSet(0).call() + const assertBalanceUnchanged = async (validator: string, blockNumber: number) => { + await assertBalanceChanged(validator, blockNumber, new BigNumber(0)) + } - assert.equal(strip0x(resultAddress), context.validators[1].address) - }) + const getExpectedTotalPayment = async (validator: string, blockNumber: number) => { + const score = new BigNumber( + (await validators.methods.getValidator(validator).call({}, blockNumber))[2] + ) + assert.isNotNaN(score) + return validatorEpochPayment.times(fromFixed(score)) + } - it('should return the last validator in the fourth place', async () => { - const resultAddress = await election.methods.validatorAddressFromCurrentSet(3).call() + for (const blockNumber of blockNumbers) { + let expectUnchangedBalances: string[] + let expectChangedBalances: string[] + if (isLastBlockOfEpoch(blockNumber, epoch)) { + expectChangedBalances = await getValidatorSetAtBlock(blockNumber) + expectUnchangedBalances = allValidators.filter((x) => !expectChangedBalances.includes(x)) + } else { + expectUnchangedBalances = allValidators + expectChangedBalances = [] + } - assert.equal(strip0x(resultAddress), context.validators[4].address) - }) + for (const validator of expectUnchangedBalances) { + await assertBalanceUnchanged(validator, blockNumber) + } - it('should revert when asked for an out of bounds validator', async function(this: any) { - this.timeout(0) - await assertRevert( - election.methods.validatorAddressFromCurrentSet(4).send({ - from: `0x${context.validators[0].address}`, - }) - ) - }) + let expectedGroupPayment = new BigNumber(0) + for (const validator of expectChangedBalances) { + const expectedTotalPayment = await getExpectedTotalPayment(validator, blockNumber) + const groupPayment = expectedTotalPayment.times(commission) + await assertBalanceChanged( + validator, + blockNumber, + expectedTotalPayment.minus(groupPayment) + ) + expectedGroupPayment = expectedGroupPayment.plus(groupPayment) + } + await assertBalanceChanged(group, blockNumber, expectedGroupPayment) + } }) - }) - describe('when the validator set is changing', () => { - const epoch = 10 - const expectedEpochMembership = new Map() - before(async function() { - this.timeout(0) - await restart() - const [groupAddress, groupPrivateKey] = await getValidatorGroupKeys() + it('should distribute epoch rewards at the end of each epoch', async () => { + const lockedGold = await kit._web3Contracts.getLockedGold() + const governance = await kit._web3Contracts.getGovernance() + const epochReward = new BigNumber(10).pow(18) + const infraReward = new BigNumber(10).pow(18) + const [group] = await validators.methods.getRegisteredValidatorGroups().call() - const groupInstance = { - name: 'validatorGroup', - validating: false, - syncmode: 'full', - port: 30325, - wsport: 8567, - privateKey: groupPrivateKey.slice(2), - peers: [await getEnode(8545)], + const assertVotesChanged = async (blockNumber: number, expected: BigNumber) => { + const currentVotes = new BigNumber( + await election.methods.getTotalVotesForGroup(group).call({}, blockNumber) + ) + const previousVotes = new BigNumber( + await election.methods.getTotalVotesForGroup(group).call({}, blockNumber - 1) + ) + assert.equal(expected.toFixed(), currentVotes.minus(previousVotes).toFixed()) } - await initAndStartGeth(context.hooks.gethBinaryPath, groupInstance) - const groupWeb3 = new Web3('ws://localhost:8567') - validators = new groupWeb3.eth.Contract( - validatorsAbi, - await getContractAddress('ValidatorsProxy') - ) - // Give the node time to sync. - await sleep(15) - const members = await getValidatorGroupMembers() - const membersToSwap = [members[0], members[1]] - // Start with 10 nodes - await removeMember(groupWeb3, groupAddress, membersToSwap[0]) - const changeValidatorSet = async (header: any) => { - // At the start of epoch N, swap members so the validator set is different for epoch N + 1. - if (header.number % epoch === 0) { - const groupMembers = await getValidatorGroupMembers() - const direction = groupMembers.includes(membersToSwap[0]) - const removedMember = direction ? membersToSwap[0] : membersToSwap[1] - const addedMember = direction ? membersToSwap[1] : membersToSwap[0] - expectedEpochMembership.set(header.number / epoch, [removedMember, addedMember]) - await removeMember(groupWeb3, groupAddress, removedMember) - await addMember(groupWeb3, groupAddress, addedMember) - const newMembers = await getValidatorGroupMembers() - assert.include(newMembers, addedMember) - assert.notInclude(newMembers, removedMember) - } + const assertGoldTokenTotalSupplyChanged = async ( + blockNumber: number, + expected: BigNumber + ) => { + const currentSupply = new BigNumber( + await goldToken.methods.totalSupply().call({}, blockNumber) + ) + const previousSupply = new BigNumber( + await goldToken.methods.totalSupply().call({}, blockNumber - 1) + ) + assert.equal(expected.toFixed(), currentSupply.minus(previousSupply).toFixed()) } - const subscription = await groupWeb3.eth.subscribe('newBlockHeaders') - subscription.on('data', changeValidatorSet) - // Wait for a few epochs while changing the validator set. - await sleep(epoch * 3) - ;(subscription as any).unsubscribe() - // Wait for the current epoch to complete. - await sleep(epoch) - }) + const assertBalanceChanged = async ( + address: string, + blockNumber: number, + expected: BigNumber + ) => { + const currentBalance = new BigNumber( + await goldToken.methods.balanceOf(address).call({}, blockNumber) + ) + const previousBalance = new BigNumber( + await goldToken.methods.balanceOf(address).call({}, blockNumber - 1) + ) + assert.equal(expected.toFixed(), currentBalance.minus(previousBalance).toFixed()) + } - it('should have produced blocks with the correct validator set', async function(this: any) { - this.timeout(0) // Disable test timeout - assert.equal(expectedEpochMembership.size, 3) - // tslint:disable-next-line: no-console - for (const [epochNumber, membership] of expectedEpochMembership) { - let containsExpectedMember = false - for (let i = epochNumber * epoch + 1; i < (epochNumber + 1) * epoch + 1; i++) { - const block = await web3.eth.getBlock(i) - assert.notEqual(block.miner.toLowerCase(), membership[1].toLowerCase()) - containsExpectedMember = - containsExpectedMember || block.miner.toLowerCase() === membership[0].toLowerCase() + const assertLockedGoldBalanceChanged = async (blockNumber: number, expected: BigNumber) => { + await assertBalanceChanged(lockedGold.options.address, blockNumber, expected) + } + + const assertGovernanceBalanceChanged = async (blockNumber: number, expected: BigNumber) => { + await assertBalanceChanged(governance.options.address, blockNumber, expected) + } + + const assertVotesUnchanged = async (blockNumber: number) => { + await assertVotesChanged(blockNumber, new BigNumber(0)) + } + + const assertGoldTokenTotalSupplyUnchanged = async (blockNumber: number) => { + await assertGoldTokenTotalSupplyChanged(blockNumber, new BigNumber(0)) + } + + const assertLockedGoldBalanceUnchanged = async (blockNumber: number) => { + await assertLockedGoldBalanceChanged(blockNumber, new BigNumber(0)) + } + + const assertGovernanceBalanceUnchanged = async (blockNumber: number) => { + await assertGovernanceBalanceChanged(blockNumber, new BigNumber(0)) + } + + for (const blockNumber of blockNumbers) { + if (isLastBlockOfEpoch(blockNumber, epoch)) { + await assertVotesChanged(blockNumber, epochReward) + await assertGoldTokenTotalSupplyChanged(blockNumber, epochReward.plus(infraReward)) + await assertLockedGoldBalanceChanged(blockNumber, epochReward) + await assertGovernanceBalanceChanged(blockNumber, infraReward) + } else { + await assertVotesUnchanged(blockNumber) + await assertGoldTokenTotalSupplyUnchanged(blockNumber) + await assertLockedGoldBalanceUnchanged(blockNumber) + await assertGovernanceBalanceUnchanged(blockNumber) } - assert.isTrue(containsExpectedMember) } }) }) - describe('when adding any block', () => { - let goldGenesisSupply: any - const addressesWithBalance: string[] = [] + describe('after the gold token smart contract is registered', () => { + let goldGenesisSupply = new BigNumber(0) beforeEach(async function(this: any) { this.timeout(0) // Disable test timeout await restart() const genesis = await importGenesis() - goldGenesisSupply = new BigNumber(0) - Object.keys(genesis.alloc).forEach((validator) => { - addressesWithBalance.push(validator) - goldGenesisSupply = goldGenesisSupply.plus(genesis.alloc[validator].balance) + Object.keys(genesis.alloc).forEach((address) => { + goldGenesisSupply = goldGenesisSupply.plus(genesis.alloc[address].balance) }) - // Block rewards are paid to governance and Locked Gold. - // Governance also receives a portion of transaction fees. - addressesWithBalance.push(await getContractAddress('GovernanceProxy')) - addressesWithBalance.push(await getContractAddress('LockedGoldProxy')) - // Some gold is sent to the reserve and exchange during migrations. - addressesWithBalance.push(await getContractAddress('ReserveProxy')) - addressesWithBalance.push(await getContractAddress('ExchangeProxy')) }) - it('should update the Celo Gold total supply correctly', async function(this: any) { - // To register a validator group, we send gold to a new address not included in - // `addressesWithBalance`. Therefore, we check the gold total supply at a block before - // that gold is sent. - // We don't set the total supply until block rewards are paid out, which can happen once - // either LockedGold or Governance are registered. - const _validators = new web3.eth.Contract( - validatorsAbi, - await getContractAddress('ValidatorsProxy') - ) - const events = await _validators.getPastEvents('OwnershipTransferred', { fromBlock: 0 }) - - const blockNumber = events[events.length - 1].blockNumber + 1 + it('should initialize the Celo Gold total supply correctly', async function(this: any) { + const events = await registry.getPastEvents('RegistryUpdated', { fromBlock: 0 }) + let blockNumber = 0 + for (const e of events) { + if (e.returnValues.identifier === 'GoldToken') { + blockNumber = e.blockNumber + break + } + } + assert.isAtLeast(blockNumber, 1) const goldTotalSupply = await goldToken.methods.totalSupply().call({}, blockNumber) - const balances = await Promise.all( - addressesWithBalance.map( - async (a: string) => new BigNumber(await web3.eth.getBalance(a, blockNumber)) - ) - ) - const expectedGoldTotalSupply = balances.reduce((total: BigNumber, b: BigNumber) => - b.plus(total) - ) - assert.isAtLeast(expectedGoldTotalSupply.toNumber(), goldGenesisSupply.toNumber()) - assert.equal(goldTotalSupply.toString(), expectedGoldTotalSupply.toString()) + assert.equal(goldTotalSupply, goldGenesisSupply.toFixed()) }) }) }) diff --git a/packages/celotool/src/e2e-tests/utils.ts b/packages/celotool/src/e2e-tests/utils.ts index aae32ca88ac..608fa7c9139 100644 --- a/packages/celotool/src/e2e-tests/utils.ts +++ b/packages/celotool/src/e2e-tests/utils.ts @@ -32,6 +32,7 @@ export interface GethTestConfig { migrate?: boolean migrateTo?: number instances: GethInstanceConfig[] + genesisConfig?: any } const TEST_DIR = '/tmp/e2e' @@ -171,13 +172,14 @@ async function setupTestDir(testDir: string) { await execCmd('mkdir', [testDir]) } -function writeGenesis(validators: Validator[], path: string) { +function writeGenesis(validators: Validator[], path: string, configOverrides: any = {}) { const genesis = generateGenesis({ validators, blockTime: 0, epoch: 10, requestTimeout: 3000, chainId: NetworkId, + ...configOverrides, }) fs.writeFileSync(path, genesis) } @@ -318,7 +320,11 @@ export async function startGeth(gethBinaryPath: string, instance: GethInstanceCo return instance } -export async function migrateContracts(validatorPrivateKeys: string[], to: number = 1000) { +export async function migrateContracts( + validatorPrivateKeys: string[], + validators: string[], + to: number = 1000 +) { const migrationOverrides = { validators: { validatorKeys: validatorPrivateKeys.map(ensure0x), @@ -326,6 +332,12 @@ export async function migrateContracts(validatorPrivateKeys: string[], to: numbe election: { minElectableValidators: '1', }, + stableToken: { + initialBalances: { + addresses: validators.map(ensure0x), + values: validators.map(() => '10000000000000000000000'), + }, + }, } const args = [ '--cwd', @@ -410,7 +422,7 @@ export function getContext(gethConfig: GethTestConfig) { } await buildGeth(gethRepoPath) await setupTestDir(TEST_DIR) - await writeGenesis(validators, GENESIS_PATH) + await writeGenesis(validators, GENESIS_PATH, gethConfig.genesisConfig) let validatorIndex = 0 for (const instance of gethConfig.instances) { if (instance.validating) { @@ -425,7 +437,11 @@ export function getContext(gethConfig: GethTestConfig) { await initAndStartGeth(gethBinaryPath, instance) } if (gethConfig.migrate || gethConfig.migrateTo) { - await migrateContracts(validatorPrivateKeys, gethConfig.migrateTo) + await migrateContracts( + validatorPrivateKeys, + validators.map((x) => x.address), + gethConfig.migrateTo + ) } await killGeth() await sleep(2) diff --git a/packages/celotool/src/e2e-tests/validator_order_tests.ts b/packages/celotool/src/e2e-tests/validator_order_tests.ts new file mode 100644 index 00000000000..2e71aafc78f --- /dev/null +++ b/packages/celotool/src/e2e-tests/validator_order_tests.ts @@ -0,0 +1,86 @@ +import { assert } from 'chai' +import _ from 'lodash' +import Web3 from 'web3' +import { getContext, GethTestConfig, sleep } from './utils' + +const VALIDATORS = 10 +const EPOCH = 20 +const EPOCHS_TO_WAIT = 3 +const BLOCK_COUNT = EPOCH * EPOCHS_TO_WAIT + +describe('governance tests', () => { + const gethConfig: GethTestConfig = { + migrateTo: 14, + instances: _.range(VALIDATORS).map((i) => ({ + name: `validator${i}`, + validating: true, + syncmode: 'full', + port: 30303 + 2 * i, + rpcport: 8545 + 2 * i, + })), + genesisConfig: { + epoch: EPOCH, + }, + } + + const context: any = getContext(gethConfig) + let web3: Web3 + + before(async function(this: any) { + this.timeout(0) + await context.hooks.before() + }) + + after(context.hooks.after) + + describe('Validator ordering', () => { + before(async function() { + this.timeout(0) + web3 = new Web3('http://localhost:8545') + await context.hooks.restart() + }) + + it('properly orders validators randomly', async function(this: any) { + this.timeout(160000 /* 160 seconds */) + + const latestBlockNumber = (await web3.eth.getBlock('latest')).number + const indexInEpoch = ((latestBlockNumber % EPOCH) + EPOCH - 1) % EPOCH + const nextEpoch = latestBlockNumber + (EPOCH - indexInEpoch) + + // Wait for enough blocks. + while ((await web3.eth.getBlock('latest')).number < nextEpoch + BLOCK_COUNT) { + await sleep(2) + } + + // Fetch the validator for each block. + const blocks = await Promise.all( + _.range(BLOCK_COUNT).map(async (i) => web3.eth.getBlock(i + nextEpoch)) + ) + const validators = blocks.map((block) => block.miner) + + // Ensure each validator has an equal number of blocks. + const expectedCount = BLOCK_COUNT / VALIDATORS + for (const [validator, count] of Object.entries(_.countBy(validators))) { + assert.equal(count, expectedCount, `${validator} should have mined ${expectedCount} blocks`) + } + + const orderings: string[][] = [] + for (let i = 0; i < EPOCHS_TO_WAIT; i++) { + const epochValidators = validators.slice(i * EPOCH, (i + 1) * EPOCH) + const ordering = epochValidators.slice(0, VALIDATORS) + + // Ensure within an epoch, ordering is consistent. + for (const [index, validator] of ordering.entries()) { + assert.equal(validator, epochValidators[VALIDATORS + index]) + } + + // Ensure each epoch has a unique ordering. + // Note: This has a 1/(VALIDATORS!) chance of failing. With 10 validators, this is negligible. + for (const prevOrdering of orderings) { + assert(!_.isEqual(prevOrdering, ordering), 'ordering is not unique') + } + orderings.push(ordering) + } + }) + }) +}) diff --git a/packages/celotool/src/e2e-tests/verify_ultralight_geth_logs.ts b/packages/celotool/src/e2e-tests/verify_ultralight_geth_logs.ts index d03d289819e..0d79f7b608b 100644 --- a/packages/celotool/src/e2e-tests/verify_ultralight_geth_logs.ts +++ b/packages/celotool/src/e2e-tests/verify_ultralight_geth_logs.ts @@ -1,16 +1,27 @@ +import GenesisBlockUtils from '@celo/walletkit/lib/src/genesis-block-utils' import { equal, notEqual } from 'assert' import * as fs from 'fs' // These tests read logs from a client which was running in Ultralight sync mode and verifies that // only epoch headers are fetched till the height block and all headers are fetched afrerwards. -describe('Ultralight tests', () => { +describe('Ultralight client', () => { + let epoch: number + + before(async () => { + const genesis = JSON.parse(await GenesisBlockUtils.getGenesisBlockAsync(argv.network)) + if (genesis.config.istanbul.epoch) { + epoch = Number(genesis.config.istanbul.epoch) + } else { + throw Error('epoch not found in genesis block') + } + }) + beforeEach(function(this: any) { this.timeout(0) }) const argv = require('minimist')(process.argv.slice(2)) const logfile = argv.gethlogfile - const epoch = parseInt(argv.epoch, 10) let origin: number = -1 let height: number = 0 @@ -51,15 +62,15 @@ describe('Ultralight tests', () => { insertedHeaderNumbers.push(headerNumber) }) - it('Sync must start from 0', () => { + it('sync must start from 0', () => { equal(origin, 0, 'Start header is not zero, it is ' + origin) }) - it('Latest known header must be non-zero', () => { + it('latest known header must be non-zero', () => { notEqual(height, 0, 'Latest known header is zero') }) - it('Height header must be fetched', () => { + it('height header must be fetched', () => { let heightHeaderFetched: boolean = false for (const headerNumber of insertedHeaderNumbers) { if (headerNumber === height) { @@ -70,7 +81,7 @@ describe('Ultralight tests', () => { equal(heightHeaderFetched, true, 'height header ' + height + ' not fetched') }) - it('Must only download epoch blocks till height', () => { + it('must only download epoch blocks till height', () => { for (const headerNumber of insertedHeaderNumbers) { if (headerNumber < height) { equal(headerNumber % epoch, 0, 'Non-epoch header below height fetched') @@ -78,7 +89,7 @@ describe('Ultralight tests', () => { } }) - it('Must fetch all headers after height', () => { + it('must fetch all headers after height', () => { for ( let i = insertedHeaderNumbers.length - 1; i >= 0 && insertedHeaderNumbers[i] > height; diff --git a/packages/celotool/src/lib/blockscout.ts b/packages/celotool/src/lib/blockscout.ts index 97d9fe9a314..ab5b4772b07 100644 --- a/packages/celotool/src/lib/blockscout.ts +++ b/packages/celotool/src/lib/blockscout.ts @@ -59,8 +59,7 @@ async function helmParameters( const params = [ `--set domain.name=${fetchEnv('CLUSTER_DOMAIN_NAME')}`, `--set blockscout.image.repository=${fetchEnv('BLOCKSCOUT_DOCKER_IMAGE_REPOSITORY')}`, - `--set blockscout.image.webTag=${fetchEnv('BLOCKSCOUT_WEB_DOCKER_IMAGE_TAG')}`, - `--set blockscout.image.indexerTag=${fetchEnv('BLOCKSCOUT_INDEXER_DOCKER_IMAGE_TAG')}`, + `--set blockscout.image.tag=${fetchEnv('BLOCKSCOUT_DOCKER_IMAGE_TAG')}`, `--set blockscout.db.username=${blockscoutDBUsername}`, `--set blockscout.db.password=${blockscoutDBPassword}`, `--set blockscout.db.connection_name=${blockscoutDBConnectionName.trim()}`, diff --git a/packages/celotool/src/lib/env-utils.ts b/packages/celotool/src/lib/env-utils.ts index 4820605f6c7..ed4bbd0a2ba 100644 --- a/packages/celotool/src/lib/env-utils.ts +++ b/packages/celotool/src/lib/env-utils.ts @@ -29,6 +29,8 @@ export enum envVar { CLUSTER_DOMAIN_NAME = 'CLUSTER_DOMAIN_NAME', ENV_TYPE = 'ENV_TYPE', EPOCH = 'EPOCH', + ETHSTATS_DOCKER_IMAGE_REPOSITORY = 'ETHSTATS_DOCKER_IMAGE_REPOSITORY', + ETHSTATS_DOCKER_IMAGE_TAG = 'ETHSTATS_DOCKER_IMAGE_TAG', ETHSTATS_WEBSOCKETSECRET = 'ETHSTATS_WEBSOCKETSECRET', GETH_ACCOUNT_SECRET = 'GETH_ACCOUNT_SECRET', GETH_BOOTNODE_DOCKER_IMAGE_REPOSITORY = 'GETH_BOOTNODE_DOCKER_IMAGE_REPOSITORY', diff --git a/packages/celotool/src/lib/ethstats.ts b/packages/celotool/src/lib/ethstats.ts index d186f8760e8..370576dd6dd 100644 --- a/packages/celotool/src/lib/ethstats.ts +++ b/packages/celotool/src/lib/ethstats.ts @@ -31,6 +31,8 @@ function helmParameters() { `--set domain.name=${fetchEnv(envVar.CLUSTER_DOMAIN_NAME)}`, `--set ethstats.createSecret=${isVmBased()}`, `--set ethstats.webSocketSecret="${fetchEnv(envVar.ETHSTATS_WEBSOCKETSECRET)}"`, + `--set ethstats.image.repository=${fetchEnv(envVar.ETHSTATS_DOCKER_IMAGE_REPOSITORY)}`, + `--set ethstats.image.tag=${fetchEnv(envVar.ETHSTATS_DOCKER_IMAGE_TAG)}`, ] } diff --git a/packages/celotool/src/lib/generate_utils.ts b/packages/celotool/src/lib/generate_utils.ts index fceaab5170b..14c6f147a72 100644 --- a/packages/celotool/src/lib/generate_utils.ts +++ b/packages/celotool/src/lib/generate_utils.ts @@ -27,6 +27,7 @@ export enum AccountType { TX_NODE = 2, BOOTNODE = 3, FAUCET = 4, + ATTESTATION = 5, } export enum ConsensusType { @@ -45,6 +46,7 @@ export const MNEMONIC_ACCOUNT_TYPE_CHOICES = [ 'tx_node', 'bootnode', 'faucet', + 'attestation', ] export const add0x = (str: string) => { @@ -212,7 +214,9 @@ export const generateGenesis = ({ genesis.difficulty = '0x1' genesis.extraData = generateIstanbulExtraData(validators) genesis.config.istanbul = { - policy: 0, + // see github.com/celo-org/celo-blockchain/blob/master/consensus/istanbul/config.go#L21-L25 + // 0 = RoundRobin, 1 = Sticky, 2 = ShuffledRoundRobin + policy: 2, period: blockTime, requesttimeout: requestTimeout, epoch, diff --git a/packages/celotool/src/lib/helm_deploy.ts b/packages/celotool/src/lib/helm_deploy.ts index dca0aa731c8..ff180d03774 100644 --- a/packages/celotool/src/lib/helm_deploy.ts +++ b/packages/celotool/src/lib/helm_deploy.ts @@ -264,10 +264,10 @@ export async function grantRoles( export async function retrieveCloudSQLConnectionInfo(celoEnv: string, instanceName: string) { await validateExistingCloudSQLInstance(instanceName) const [blockscoutDBUsername] = await execCmdWithExitOnFailure( - `kubectl get secret ${celoEnv}-blockscout --export -o jsonpath='{.data.DB_USERNAME}' -n ${celoEnv} | base64 --decode` + `kubectl get secret ${celoEnv}-blockscout --export -o jsonpath='{.data.DATABASE_USER}' -n ${celoEnv} | base64 --decode` ) const [blockscoutDBPassword] = await execCmdWithExitOnFailure( - `kubectl get secret ${celoEnv}-blockscout --export -o jsonpath='{.data.DB_PASSWORD}' -n ${celoEnv} | base64 --decode` + `kubectl get secret ${celoEnv}-blockscout --export -o jsonpath='{.data.DATABASE_PASSWORD}' -n ${celoEnv} | base64 --decode` ) const [blockscoutDBConnectionName] = await execCmdWithExitOnFailure( `gcloud sql instances describe ${instanceName} --format="value(connectionName)"` diff --git a/packages/cli/src/commands/lockedgold/authorize.ts b/packages/cli/src/commands/account/authorize.ts similarity index 61% rename from packages/cli/src/commands/lockedgold/authorize.ts rename to packages/cli/src/commands/account/authorize.ts index aae72011553..eaf496f0ffa 100644 --- a/packages/cli/src/commands/lockedgold/authorize.ts +++ b/packages/cli/src/commands/account/authorize.ts @@ -4,14 +4,14 @@ import { displaySendTx } from '../../utils/cli' import { Flags } from '../../utils/command' export default class Authorize extends BaseCommand { - static description = 'Authorize validating or voting address for a Locked Gold account' + static description = 'Authorize an attestation, validation or vote signing key' static flags = { ...BaseCommand.flags, from: Flags.address({ required: true }), role: flags.string({ char: 'r', - options: ['voter', 'validator'], + options: ['vote', 'validation', 'attestation'], description: 'Role to delegate', }), to: Flags.address({ required: true }), @@ -20,7 +20,7 @@ export default class Authorize extends BaseCommand { static args = [] static examples = [ - 'authorize --from 0x5409ED021D9299bf6814279A6A1411A7e866A631 --role voter --to 0xc1912fEE45d61C87Cc5EA59DaE31190FFFFf232d', + 'authorize --from 0x5409ED021D9299bf6814279A6A1411A7e866A631 --role vote --to 0xc1912fEE45d61C87Cc5EA59DaE31190FFFFf232d', ] async run() { @@ -37,12 +37,14 @@ export default class Authorize extends BaseCommand { } this.kit.defaultAccount = res.flags.from - const lockedGold = await this.kit.contracts.getLockedGold() + const accounts = await this.kit.contracts.getAccounts() let tx: any - if (res.flags.role === 'voter') { - tx = await lockedGold.authorizeVoter(res.flags.from, res.flags.to) - } else if (res.flags.role === 'validator') { - tx = await lockedGold.authorizeValidator(res.flags.from, res.flags.to) + if (res.flags.role === 'vote') { + tx = await accounts.authorizeVoteSigner(res.flags.from, res.flags.to) + } else if (res.flags.role === 'validation') { + tx = await accounts.authorizeValidationSigner(res.flags.from, res.flags.to) + } else if (res.flags.role === 'attestation') { + tx = await accounts.authorizeAttestationSigner(res.flags.from, res.flags.to) } else { this.error(`Invalid role provided`) return diff --git a/packages/cli/src/commands/identity/change-attestation-service-url.ts b/packages/cli/src/commands/account/change-attestation-service-url.ts similarity index 100% rename from packages/cli/src/commands/identity/change-attestation-service-url.ts rename to packages/cli/src/commands/account/change-attestation-service-url.ts diff --git a/packages/cli/src/commands/identity/change-domain.ts b/packages/cli/src/commands/account/change-domain.ts similarity index 100% rename from packages/cli/src/commands/identity/change-domain.ts rename to packages/cli/src/commands/account/change-domain.ts diff --git a/packages/cli/src/commands/identity/change-name.ts b/packages/cli/src/commands/account/change-name.ts similarity index 100% rename from packages/cli/src/commands/identity/change-name.ts rename to packages/cli/src/commands/account/change-name.ts diff --git a/packages/cli/src/commands/identity/create-metadata.ts b/packages/cli/src/commands/account/create-metadata.ts similarity index 100% rename from packages/cli/src/commands/identity/create-metadata.ts rename to packages/cli/src/commands/account/create-metadata.ts diff --git a/packages/cli/src/commands/identity/get-metadata.ts b/packages/cli/src/commands/account/get-metadata.ts similarity index 88% rename from packages/cli/src/commands/identity/get-metadata.ts rename to packages/cli/src/commands/account/get-metadata.ts index 30f03a832d0..2cef83a565e 100644 --- a/packages/cli/src/commands/identity/get-metadata.ts +++ b/packages/cli/src/commands/account/get-metadata.ts @@ -18,8 +18,8 @@ export default class GetMetadata extends BaseCommand { async run() { const { args } = this.parse(GetMetadata) const address = args.address - const attestations = await this.kit.contracts.getAttestations() - const metadataURL = await attestations.getMetadataURL(address) + const accounts = await this.kit.contracts.getAccounts() + const metadataURL = await accounts.getMetadataURL(address) if (!metadataURL) { console.info('No metadata set for address') diff --git a/packages/cli/src/commands/identity/register-metadata.ts b/packages/cli/src/commands/account/register-metadata.ts similarity index 82% rename from packages/cli/src/commands/identity/register-metadata.ts rename to packages/cli/src/commands/account/register-metadata.ts index a2643e13936..57e49cbaeb3 100644 --- a/packages/cli/src/commands/identity/register-metadata.ts +++ b/packages/cli/src/commands/account/register-metadata.ts @@ -22,7 +22,7 @@ export default class RegisterMetadata extends BaseCommand { async run() { const { flags } = this.parse(RegisterMetadata) this.kit.defaultAccount = flags.from - const attestations = await this.kit.contracts.getAttestations() - await displaySendTx('registerMetadata', attestations.setMetadataURL(flags.url)) + const accounts = await this.kit.contracts.getAccounts() + await displaySendTx('registerMetadata', accounts.setMetadataURL(flags.url)) } } diff --git a/packages/cli/src/commands/lockedgold/register.ts b/packages/cli/src/commands/account/register.ts similarity index 58% rename from packages/cli/src/commands/lockedgold/register.ts rename to packages/cli/src/commands/account/register.ts index 04a73d9c0f5..b12462caecf 100644 --- a/packages/cli/src/commands/lockedgold/register.ts +++ b/packages/cli/src/commands/account/register.ts @@ -1,12 +1,14 @@ +import { flags } from '@oclif/command' import { BaseCommand } from '../../base' import { displaySendTx } from '../../utils/cli' import { Flags } from '../../utils/command' export default class Register extends BaseCommand { - static description = 'Register an account for Locked Gold' + static description = 'Register an account' static flags = { ...BaseCommand.flags, + name: flags.string({ required: true }), from: Flags.address({ required: true }), } @@ -17,7 +19,8 @@ export default class Register extends BaseCommand { async run() { const res = this.parse(Register) this.kit.defaultAccount = res.flags.from - const lockedGold = await this.kit.contracts.getLockedGold() - await displaySendTx('register', lockedGold.createAccount()) + const accounts = await this.kit.contracts.getAccounts() + await displaySendTx('register', accounts.createAccount()) + await displaySendTx('setName', accounts.setName(name)) } } diff --git a/packages/cli/src/commands/identity/show-metadata.ts b/packages/cli/src/commands/account/show-metadata.ts similarity index 100% rename from packages/cli/src/commands/identity/show-metadata.ts rename to packages/cli/src/commands/account/show-metadata.ts diff --git a/packages/cli/src/commands/validator/list.ts b/packages/cli/src/commands/validator/list.ts index 2e4efa37e11..af742764809 100644 --- a/packages/cli/src/commands/validator/list.ts +++ b/packages/cli/src/commands/validator/list.ts @@ -21,7 +21,6 @@ export default class ValidatorList extends BaseCommand { cli.table(validatorList, { address: {}, name: {}, - url: {}, publicKey: {}, affiliation: {}, }) diff --git a/packages/cli/src/commands/validator/register.ts b/packages/cli/src/commands/validator/register.ts index ea44245a896..91b569bd255 100644 --- a/packages/cli/src/commands/validator/register.ts +++ b/packages/cli/src/commands/validator/register.ts @@ -1,4 +1,3 @@ -import { flags } from '@oclif/command' import { BaseCommand } from '../../base' import { displaySendTx } from '../../utils/cli' import { Flags } from '../../utils/command' @@ -10,29 +9,27 @@ export default class ValidatorRegister extends BaseCommand { static flags = { ...BaseCommand.flags, from: Flags.address({ required: true, description: 'Address for the Validator' }), - name: flags.string({ required: true }), - url: flags.string({ required: true }), publicKey: Flags.publicKey({ required: true }), } static examples = [ - 'register --from 0x47e172F6CfB6c7D01C1574fa3E2Be7CC73269D95 --name myName --url "http://validator.com" --publicKey 0xc52f3fab06e22a54915a8765c4f6826090cfac5e40282b43844bf1c0df83aaa632e55b67869758f2291d1aabe0ebecc7cbf4236aaa45e3e0cfbf997eda082ae19d3e1d8f49f6b0d8e9a03d80ca07b1d24cf1cc0557bdcc04f5e17a46e35d02d0d411d956dbd5d2d2464eebd7b74ae30005d223780d785d2abc5644fac7ac29fb0e302bdc80c81a5d45018b68b1045068a4b3a4861c93037685fd0d252d7405011220a66a6257562d0c26dabf64485a1d96bad27bb1c0fd6080a75b0ec9f75b50298a2a8e04b02b2688c8104fca61fb00', + 'register --from 0x47e172F6CfB6c7D01C1574fa3E2Be7CC73269D95 --publicKey 0xc52f3fab06e22a54915a8765c4f6826090cfac5e40282b43844bf1c0df83aaa632e55b67869758f2291d1aabe0ebecc7cbf4236aaa45e3e0cfbf997eda082ae19d3e1d8f49f6b0d8e9a03d80ca07b1d24cf1cc0557bdcc04f5e17a46e35d02d0d411d956dbd5d2d2464eebd7b74ae30005d223780d785d2abc5644fac7ac29fb0e302bdc80c81a5d45018b68b1045068a4b3a4861c93037685fd0d252d7405011220a66a6257562d0c26dabf64485a1d96bad27bb1c0fd6080a75b0ec9f75b50298a2a8e04b02b2688c8104fca61fb00', ] async run() { const res = this.parse(ValidatorRegister) this.kit.defaultAccount = res.flags.from const validators = await this.kit.contracts.getValidators() - const attestations = await this.kit.contracts.getAttestations() + const accounts = await this.kit.contracts.getAccounts() await displaySendTx( 'registerValidator', - validators.registerValidator(res.flags.name, res.flags.url, res.flags.publicKey as any) + validators.registerValidator(res.flags.publicKey as any) ) - // register encryption key on attestations contract + // register encryption key on accounts contract // TODO: Use a different key data encryption const pubKey = await getPubKeyFromAddrAndWeb3(res.flags.from, this.web3) // TODO fix typing - const setKeyTx = attestations.setAccountDataEncryptionKey(pubKey as any) + const setKeyTx = accounts.setAccountDataEncryptionKey(pubKey as any) await displaySendTx('Set encryption key', setKeyTx) } } diff --git a/packages/cli/src/commands/validatorgroup/list.ts b/packages/cli/src/commands/validatorgroup/list.ts index 7743ab63f9a..096520b8ff9 100644 --- a/packages/cli/src/commands/validatorgroup/list.ts +++ b/packages/cli/src/commands/validatorgroup/list.ts @@ -21,7 +21,6 @@ export default class ValidatorGroupList extends BaseCommand { cli.table(vgroups, { address: {}, name: {}, - url: {}, commission: { get: (r) => r.commission.toFixed() }, members: { get: (r) => r.members.length }, }) diff --git a/packages/cli/src/commands/validatorgroup/member.ts b/packages/cli/src/commands/validatorgroup/member.ts index 86f8ab0df03..5470ece4b67 100644 --- a/packages/cli/src/commands/validatorgroup/member.ts +++ b/packages/cli/src/commands/validatorgroup/member.ts @@ -42,14 +42,9 @@ export default class ValidatorGroupMembers extends BaseCommand { this.kit.defaultAccount = res.flags.from const validators = await this.kit.contracts.getValidators() - const election = await this.kit.contracts.getElection() - if (res.flags.accept) { - await displaySendTx('addMember', validators.addMember((res.args as any).validatorAddress)) - if ((await validators.getGroupNumMembers(res.flags.from)).isEqualTo(1)) { - const tx = await election.markGroupEligible(res.flags.from) - await displaySendTx('markGroupEligible', tx) - } + const tx = await validators.addMember(res.flags.from, (res.args as any).validatorAddress) + await displaySendTx('addMember', tx) } else if (res.flags.remove) { await displaySendTx( 'removeMember', diff --git a/packages/cli/src/commands/validatorgroup/register.ts b/packages/cli/src/commands/validatorgroup/register.ts index 365bc9cf7f6..1580cfaca30 100644 --- a/packages/cli/src/commands/validatorgroup/register.ts +++ b/packages/cli/src/commands/validatorgroup/register.ts @@ -10,13 +10,11 @@ export default class ValidatorGroupRegister extends BaseCommand { static flags = { ...BaseCommand.flags, from: Flags.address({ required: true, description: 'Address for the Validator Group' }), - name: flags.string({ required: true }), - url: flags.string({ required: true }), commission: flags.string({ required: true }), } static examples = [ - 'register --from 0x47e172F6CfB6c7D01C1574fa3E2Be7CC73269D95 --name myName --url "http://vgroup.com" --commission 0.1', + 'register --from 0x47e172F6CfB6c7D01C1574fa3E2Be7CC73269D95 --name myName --commission 0.1', ] async run() { @@ -24,11 +22,7 @@ export default class ValidatorGroupRegister extends BaseCommand { this.kit.defaultAccount = res.flags.from const validators = await this.kit.contracts.getValidators() - const tx = await validators.registerValidatorGroup( - res.flags.name, - res.flags.url, - new BigNumber(res.flags.commission) - ) + const tx = await validators.registerValidatorGroup(new BigNumber(res.flags.commission)) await displaySendTx('registerValidatorGroup', tx) } } diff --git a/packages/contractkit/README.md b/packages/contractkit/README.md index de4db7fb5b2..1f357c9a263 100644 --- a/packages/contractkit/README.md +++ b/packages/contractkit/README.md @@ -30,7 +30,7 @@ To start working with contractkit you need a `kit` instance: ```ts import { newKit } from '@celo/contractkit' -const kit = newKit('https://alfajores-infura.celo-testnet.org:8545') +const kit = newKit('https://alfajores-forno.celo-testnet.org:8545') ``` To access web3: @@ -47,7 +47,7 @@ await kit.web3.eth.getBalance(someAddress) import { newKit, CeloContract } from '@celo/contractkit' async function getKit(myAddress: string) { - const kit = newKit('https://alfajores-infura.celo-testnet.org:8545') + const kit = newKit('https://alfajores-forno.celo-testnet.org:8545') // default from kit.defaultAccount = myAddress @@ -92,6 +92,7 @@ Apart from GoldToken and StableToken, there are many core contracts. For the moment, we have contract wrappers for: +- Accounts - Exchange (Uniswap kind exchange between Gold and Stable tokens) - Validators - LockedGold @@ -115,6 +116,7 @@ We expose native wrappers for all Celo core contracts. The complete list of Celo Core contracts is: +- Accounts - Attestations - LockedGold - Escrow diff --git a/packages/contractkit/package.json b/packages/contractkit/package.json index d461819180e..becf434c741 100644 --- a/packages/contractkit/package.json +++ b/packages/contractkit/package.json @@ -20,7 +20,7 @@ "clean:all": "yarn clean && rm -rf src/generated", "build:gen": "yarn --cwd ../protocol build", "prepublishOnly": "yarn build:gen && yarn build", - "test:prepare": "yarn --cwd ../protocol devchain generate .devchain", + "test:prepare": "yarn --cwd ../protocol devchain generate .devchain --migration_override src/test-utils/migration-override.json", "test": "jest --runInBand", "lint": "tslint -c tslint.json --project ." }, @@ -31,7 +31,7 @@ "bignumber.js": "^7.2.0", "cross-fetch": "3.0.4", "debug": "^4.1.1", - "fp-ts": "2.0.5", + "fp-ts": "2.1.1", "eth-lib": "^0.2.8", "io-ts": "2.0.1", "web3": "1.0.0-beta.37", @@ -40,7 +40,7 @@ "web3-eth-abi": "1.0.0-beta.37" }, "devDependencies": { - "@celo/ganache-cli": "git+https://github.com/celo-org/ganache-cli.git#98ad2ba", + "@celo/ganache-cli": "git+https://github.com/celo-org/ganache-cli.git#9d77e02", "@celo/protocol": "1.0.0", "@types/debug": "^4.1.5", "@types/web3": "^1.0.18", diff --git a/packages/contractkit/src/base.ts b/packages/contractkit/src/base.ts index a410cbb7fac..b9c2cbed506 100644 --- a/packages/contractkit/src/base.ts +++ b/packages/contractkit/src/base.ts @@ -1,6 +1,7 @@ export type Address = string export enum CeloContract { + Accounts = 'Accounts', Attestations = 'Attestations', Election = 'Election', Escrow = 'Escrow', diff --git a/packages/contractkit/src/contract-cache.ts b/packages/contractkit/src/contract-cache.ts index c93a5cf7713..94e35fa7ac5 100644 --- a/packages/contractkit/src/contract-cache.ts +++ b/packages/contractkit/src/contract-cache.ts @@ -1,5 +1,6 @@ import { CeloContract } from './base' import { ContractKit } from './kit' +import { AccountsWrapper } from './wrappers/Accounts' import { AttestationsWrapper } from './wrappers/Attestations' import { ElectionWrapper } from './wrappers/Election' import { ExchangeWrapper } from './wrappers/Exchange' @@ -13,6 +14,7 @@ import { StableTokenWrapper } from './wrappers/StableTokenWrapper' import { ValidatorsWrapper } from './wrappers/Validators' const WrapperFactories = { + [CeloContract.Accounts]: AccountsWrapper, [CeloContract.Attestations]: AttestationsWrapper, [CeloContract.Election]: ElectionWrapper, // [CeloContract.Escrow]: EscrowWrapper, @@ -35,6 +37,7 @@ type CFType = typeof WrapperFactories export type ValidWrappers = keyof CFType interface WrapperCacheMap { + [CeloContract.Accounts]?: AccountsWrapper [CeloContract.Attestations]?: AttestationsWrapper [CeloContract.Election]?: ElectionWrapper // [CeloContract.Escrow]?: EscrowWrapper, @@ -64,6 +67,9 @@ export class WrapperCache { constructor(readonly kit: ContractKit) {} + getAccounts() { + return this.getContract(CeloContract.Accounts) + } getAttestations() { return this.getContract(CeloContract.Attestations) } diff --git a/packages/contractkit/src/test-utils/ganache-test.ts b/packages/contractkit/src/test-utils/ganache-test.ts index 977f980db3f..d1805081f8c 100644 --- a/packages/contractkit/src/test-utils/ganache-test.ts +++ b/packages/contractkit/src/test-utils/ganache-test.ts @@ -1,7 +1,14 @@ +import * as fs from 'fs' import Web3 from 'web3' import { JsonRPCResponse } from 'web3/providers' import { injectDebugProvider } from '../providers/debug-provider' +// This file specifies accounts available when ganache is running. These are derived +// from the MNEMONIC +export const NetworkConfig = JSON.parse( + fs.readFileSync('src/test-utils/migration-override.json').toString() +) + export function jsonRpcCall(web3: Web3, method: string, params: any[]): Promise { return new Promise((resolve, reject) => { web3.currentProvider.send( diff --git a/packages/contractkit/src/test-utils/ganache.setup.ts b/packages/contractkit/src/test-utils/ganache.setup.ts index de02dd978c3..44cbb558393 100644 --- a/packages/contractkit/src/test-utils/ganache.setup.ts +++ b/packages/contractkit/src/test-utils/ganache.setup.ts @@ -3,7 +3,30 @@ import * as ganache from '@celo/ganache-cli' import * as path from 'path' const MNEMONIC = 'concert load couple harbor equip island argue ramp clarify fence smart topic' - +export const ACCOUNT_PRIVATE_KEYS = [ + '0xf2f48ee19680706196e2e339e5da3491186e0c4c5030670656b0e0164837257d', + '0x5d862464fe9303452126c8bc94274b8c5f9874cbd219789b3eb2128075a76f72', + '0xdf02719c4df8b9b8ac7f551fcb5d9ef48fa27eef7a66453879f4d8fdc6e78fb1', + '0xff12e391b79415e941a94de3bf3a9aee577aed0731e297d5cfa0b8a1e02fa1d0', + '0x752dd9cf65e68cfaba7d60225cbdbc1f4729dd5e5507def72815ed0d8abc6249', + '0xefb595a0178eb79a8df953f87c5148402a224cdf725e88c0146727c6aceadccd', + '0x83c6d2cc5ddcf9711a6d59b417dc20eb48afd58d45290099e5987e3d768f328f', + '0xbb2d3f7c9583780a7d3904a2f55d792707c345f21de1bacb2d389934d82796b2', + '0xb2fd4d29c1390b71b8795ae81196bfd60293adf99f9d32a0aff06288fcdac55f', + '0x23cb7121166b9a2f93ae0b7c05bde02eae50d64449b2cbb42bc84e9d38d6cc89', +] +export const ACCOUNT_ADDRESSES = [ + '0x5409ED021D9299bf6814279A6A1411A7e866A631', + '0x6Ecbe1DB9EF729CBe972C83Fb886247691Fb6beb', + '0xE36Ea790bc9d7AB70C55260C66D52b1eca985f84', + '0xE834EC434DABA538cd1b9Fe1582052B880BD7e63', + '0x78dc5D2D739606d31509C31d654056A45185ECb6', + '0xA8dDa8d7F5310E4A9E24F8eBA77E091Ac264f872', + '0x06cEf8E666768cC40Cc78CF93d9611019dDcB628', + '0x4404ac8bd8F9618D27Ad2f1485AA1B2cFD82482D', + '0x7457d5E02197480Db681D3fdF256c7acA21bDc12', + '0x91c987bf62D25945dB517BDAa840A6c661374402', +] export async function startGanache(datadir: string, opts: { verbose?: boolean } = {}) { const logFn = opts.verbose ? // tslint:disable-next-line: no-console @@ -20,7 +43,7 @@ export async function startGanache(datadir: string, opts: { verbose?: boolean } network_id: 1101, db_path: datadir, mnemonic: MNEMONIC, - gasLimit: 7000000, + gasLimit: 10000000, allowUnlimitedContractSize: true, }) diff --git a/packages/contractkit/src/test-utils/migration-override.json b/packages/contractkit/src/test-utils/migration-override.json new file mode 100644 index 00000000000..7e2bec1145d --- /dev/null +++ b/packages/contractkit/src/test-utils/migration-override.json @@ -0,0 +1,14 @@ +{ + "stableToken": { + "initialBalances": { + "addresses": ["0x5409ed021d9299bf6814279a6a1411a7e866a631"], + "values": ["10000000000000000000000"] + }, + "oracles": [ + "0x5409ED021D9299bf6814279A6A1411A7e866A631", + "0xE36Ea790bc9d7AB70C55260C66D52b1eca985f84", + "0x06cEf8E666768cC40Cc78CF93d9611019dDcB628", + "0x7457d5E02197480Db681D3fdF256c7acA21bDc12" + ] + } +} diff --git a/packages/contractkit/src/utils/signing.test.ts b/packages/contractkit/src/utils/signing.test.ts new file mode 100644 index 00000000000..9940d9e1043 --- /dev/null +++ b/packages/contractkit/src/utils/signing.test.ts @@ -0,0 +1,35 @@ +import { LocalSigner, NativeSigner, parseSignature } from '@celo/utils/lib/signatureUtils' +import { testWithGanache } from '../test-utils/ganache-test' +import { ACCOUNT_ADDRESSES, ACCOUNT_PRIVATE_KEYS } from '../test-utils/ganache.setup' + +// This only really tests signatureUtils in @celo/utils, but is tested here +// to avoid the web3/ganache setup in @celo/utils +testWithGanache('Signing', (web3) => { + const account = ACCOUNT_ADDRESSES[0] + const pKey = ACCOUNT_PRIVATE_KEYS[0] + + const nativeSigner = NativeSigner(web3.eth.sign, account) + const localSigner = LocalSigner(pKey) + + it('signs a message the same way via RPC and with an explicit private key', async () => { + const message = 'message' + const nativeSignature = await nativeSigner.sign(message) + const localSignature = await localSigner.sign(message) + + expect(parseSignature(message, nativeSignature, account)).toEqual( + parseSignature(message, localSignature, account) + ) + }) + + it('signs a message that was hashed the same way via RPC and with an explicit private key', async () => { + // This test checks that the prefixing in `signMessage` appropriately considers hex strings + // as bytes the same way the native RPC signing would + const message = web3.utils.soliditySha3('message') + const nativeSignature = await nativeSigner.sign(message) + const localSignature = await localSigner.sign(message) + + expect(parseSignature(message, nativeSignature, account)).toEqual( + parseSignature(message, localSignature, account) + ) + }) +}) diff --git a/packages/contractkit/src/web3-contract-cache.ts b/packages/contractkit/src/web3-contract-cache.ts index 35d705170eb..546853b6603 100644 --- a/packages/contractkit/src/web3-contract-cache.ts +++ b/packages/contractkit/src/web3-contract-cache.ts @@ -1,5 +1,6 @@ import debugFactory from 'debug' import { CeloContract } from './base' +import { newAccounts } from './generated/Accounts' import { newAttestations } from './generated/Attestations' import { newElection } from './generated/Election' import { newEscrow } from './generated/Escrow' @@ -20,6 +21,7 @@ import { ContractKit } from './kit' const debug = debugFactory('kit:web3-contract-cache') const ContractFactories = { + [CeloContract.Accounts]: newAccounts, [CeloContract.Attestations]: newAttestations, [CeloContract.Election]: newElection, [CeloContract.Escrow]: newEscrow, @@ -52,7 +54,9 @@ export class Web3ContractCache { private cacheMap: ContractCacheMap = {} constructor(readonly kit: ContractKit) {} - + getAccounts() { + return this.getContract(CeloContract.Accounts) + } getAttestations() { return this.getContract(CeloContract.Attestations) } diff --git a/packages/contractkit/src/wrappers/Accounts.ts b/packages/contractkit/src/wrappers/Accounts.ts new file mode 100644 index 00000000000..a13aec9b0d1 --- /dev/null +++ b/packages/contractkit/src/wrappers/Accounts.ts @@ -0,0 +1,176 @@ +import Web3 from 'web3' +import { Address } from '../base' +import { Accounts } from '../generated/types/Accounts' +import { + BaseWrapper, + CeloTransactionObject, + proxyCall, + proxySend, + toTransactionObject, +} from '../wrappers/BaseWrapper' + +enum SignerRole { + Attestation, + Validation, + Vote, +} +/** + * Contract for handling deposits needed for voting. + */ +export class AccountsWrapper extends BaseWrapper { + /** + * Creates an account. + */ + createAccount = proxySend(this.kit, this.contract.methods.createAccount) + + /** + * Returns the attestation signer for the specified account. + * @param account The address of the account. + * @return The address with which the account can vote. + */ + getAttestationSigner: (account: string) => Promise
= proxyCall( + this.contract.methods.getAttestationSigner + ) + /** + * Returns the vote signer for the specified account. + * @param account The address of the account. + * @return The address with which the account can vote. + */ + getVoteSigner: (account: string) => Promise
= proxyCall( + this.contract.methods.getVoteSigner + ) + /** + * Returns the validation signere for the specified account. + * @param account The address of the account. + * @return The address with which the account can register a validator or group. + */ + getValidationSigner: (account: string) => Promise
= proxyCall( + this.contract.methods.getValidationSigner + ) + + /** + * Check if an account already exists. + * @param account The address of the account + * @return Returns `true` if account exists. Returns `false` otherwise. + */ + isAccount: (account: string) => Promise = proxyCall(this.contract.methods.isAccount) + + /** + * Authorize an attestation signing key on behalf of this account to another address. + * @param account Address of the active account. + * @param attestationSigner The address of the signing key to authorize. + * @return A CeloTransactionObject + */ + async authorizeAttestationSigner( + account: Address, + attestationSigner: Address + ): Promise> { + return this.authorizeSigner(SignerRole.Attestation, account, attestationSigner) + } + /** + * Authorizes an address to sign votes on behalf of the account. + * @param account Address of the active account. + * @param voteSigner The address of the vote signing key to authorize. + * @return A CeloTransactionObject + */ + async authorizeVoteSigner( + account: Address, + voteSigner: Address + ): Promise> { + return this.authorizeSigner(SignerRole.Vote, account, voteSigner) + } + + /** + * Authorizes an address to sign consensus messages on behalf of the account. + * @param account Address of the active account. + * @param validationSigner The address of the signing key to authorize. + * @return A CeloTransactionObject + */ + async authorizeValidationSigner( + account: Address, + validationSigner: Address + ): Promise> { + return this.authorizeSigner(SignerRole.Validation, account, validationSigner) + } + + /** + * Returns the set name for the account + * @param account Account + */ + getName = proxyCall(this.contract.methods.getName) + + /** + * Returns the set data encryption key for the account + * @param account Account + */ + getDataEncryptionKey = proxyCall(this.contract.methods.getDataEncryptionKey) + + /** + * Returns the set wallet address for the account + * @param account Account + */ + getWalletAddress = proxyCall(this.contract.methods.getWalletAddress) + + /** + * Returns the metadataURL for the account + * @param account Account + */ + getMetadataURL = proxyCall(this.contract.methods.getMetadataURL) + + /** + * Sets the data encryption of the account + * @param encryptionKey The key to set + */ + setAccountDataEncryptionKey = proxySend( + this.kit, + this.contract.methods.setAccountDataEncryptionKey + ) + + /** + * Convenience Setter for the dataEncryptionKey and wallet address for an account + * @param name A string to set as the name of the account + * @param dataEncryptionKey secp256k1 public key for data encryption. Preferably compressed. + * @param walletAddress The wallet address to set for the account + */ + setAccount = proxySend(this.kit, this.contract.methods.setAccount) + + /** + * Sets the name for the account + * @param name The name to set + */ + setName = proxySend(this.kit, this.contract.methods.setName) + + /** + * Sets the metadataURL for the account + * @param url The url to set + */ + setMetadataURL = proxySend(this.kit, this.contract.methods.setMetadataURL) + + /** + * Sets the wallet address for the account + * @param address The address to set + */ + setWalletAddress = proxySend(this.kit, this.contract.methods.setWalletAddress) + + private authorizeFns = { + [SignerRole.Attestation]: this.contract.methods.authorizeAttestationSigner, + [SignerRole.Validation]: this.contract.methods.authorizeValidationSigner, + [SignerRole.Vote]: this.contract.methods.authorizeVoteSigner, + } + + private async authorizeSigner(role: SignerRole, account: Address, signer: Address) { + const sig = await this.getParsedSignatureOfAddress(account, signer) + // TODO(asa): Pass default tx "from" argument. + return toTransactionObject(this.kit, this.authorizeFns[role](signer, sig.v, sig.r, sig.s)) + } + + private async getParsedSignatureOfAddress(address: Address, signer: string) { + const hash = Web3.utils.soliditySha3({ type: 'address', value: address }) + const signature = (await this.kit.web3.eth.sign(hash, signer)).slice(2) + return { + r: `0x${signature.slice(0, 64)}`, + s: `0x${signature.slice(64, 128)}`, + v: Web3.utils.hexToNumber(signature.slice(128, 130)) + 27, + } + } +} diff --git a/packages/contractkit/src/wrappers/Attestations.ts b/packages/contractkit/src/wrappers/Attestations.ts index dc78173ebb5..701cd14e68e 100644 --- a/packages/contractkit/src/wrappers/Attestations.ts +++ b/packages/contractkit/src/wrappers/Attestations.ts @@ -1,13 +1,13 @@ import { ECIES, PhoneNumberUtils, SignatureUtils } from '@celo/utils' +import { sleep } from '@celo/utils/lib/async' import { zip3 } from '@celo/utils/lib/collections' import BigNumber from 'bignumber.js' import * as Web3Utils from 'web3-utils' -import { Address, CeloContract } from '../base' +import { Address, CeloContract, NULL_ADDRESS } from '../base' import { Attestations } from '../generated/types/Attestations' import { BaseWrapper, proxyCall, - proxySend, toBigNumber, toNumber, toTransactionObject, @@ -20,13 +20,17 @@ export interface AttestationStat { total: number } +export interface AttestationStateForIssuer { + attestationState: AttestationState +} + export interface AttestationsToken { address: Address fee: BigNumber } export interface AttestationsConfig { - attestationExpirySeconds: number + attestationExpiryBlocks: number attestationRequestFees: AttestationsToken[] } @@ -42,13 +46,13 @@ export enum AttestationState { export interface ActionableAttestation { issuer: Address attestationState: AttestationState - time: number + blockNumber: number publicKey: string } const parseAttestationInfo = (rawState: { 0: string; 1: string }) => ({ attestationState: parseInt(rawState[0], 10), - time: parseInt(rawState[1], 10), + blockNumber: parseInt(rawState[1], 10), }) function attestationMessageToSign(phoneHash: string, account: Address) { @@ -59,12 +63,13 @@ function attestationMessageToSign(phoneHash: string, account: Address) { return messageHash } +const stringIdentity = (x: string) => x export class AttestationsWrapper extends BaseWrapper { /** * Returns the time an attestation can be completable before it is considered expired */ - attestationExpirySeconds = proxyCall( - this.contract.methods.attestationExpirySeconds, + attestationExpiryBlocks = proxyCall( + this.contract.methods.attestationExpiryBlocks, undefined, toNumber ) @@ -80,53 +85,79 @@ export class AttestationsWrapper extends BaseWrapper { toBigNumber ) + selectIssuersWaitBlocks = proxyCall( + this.contract.methods.selectIssuersWaitBlocks, + undefined, + toNumber + ) + /** - * Returns the attestation stats of a phone number/account pair - * @param phoneNumber Phone Number - * @param account Account + * @notice Returns the unselected attestation request for an identifier/account pair, if any. + * @param identifier Hash of the identifier. + * @param account Address of the account. */ - getAttestationStat: ( - phoneNumber: string, - account: Address - ) => Promise = proxyCall( - this.contract.methods.getAttestationStats, + getUnselectedRequest = proxyCall( + this.contract.methods.getUnselectedRequest, tupleParser(PhoneNumberUtils.getPhoneHash, (x: string) => x), - (stat) => ({ completed: toNumber(stat[0]), total: toNumber(stat[1]) }) + (res) => ({ + blockNumber: toNumber(res[0]), + attestationsRequested: toNumber(res[1]), + attestationRequestFeeToken: res[2], + }) ) + waitForSelectingIssuers = async ( + phoneNumber: string, + account: Address, + timeoutSeconds = 120, + pollDurationSeconds = 1 + ) => { + const startTime = Date.now() + const unselectedRequest = await this.getUnselectedRequest(phoneNumber, account) + const waitBlocks = await this.selectIssuersWaitBlocks() + + if (unselectedRequest.blockNumber === 0) { + throw new Error('No unselectedRequest to wait for') + } + // Technically should use subscriptions here but not all providers support it. + // TODO: Use subscription if provider supports + while (Date.now() - startTime < timeoutSeconds * 1000) { + const blockNumber = await this.kit.web3.eth.getBlockNumber() + if (blockNumber >= unselectedRequest.blockNumber + waitBlocks) { + return + } + await sleep(pollDurationSeconds * 1000) + } + } /** - * Returns the set wallet address for the account + * Returns the attestation state of a phone number/account/issuer tuple + * @param phoneNumber Phone Number * @param account Account */ - getWalletAddress = proxyCall(this.contract.methods.getWalletAddress) + getAttestationState: ( + phoneNumber: string, + account: Address, + issuer: Address + ) => Promise = proxyCall( + this.contract.methods.getAttestationState, + tupleParser(PhoneNumberUtils.getPhoneHash, stringIdentity, stringIdentity), + (state) => ({ attestationState: parseInt(state[0], 10) }) + ) /** - * Returns the metadataURL for the account + * Returns the attestation stats of a phone number/account pair + * @param phoneNumber Phone Number * @param account Account */ - getMetadataURL = proxyCall(this.contract.methods.getMetadataURL) - - /** - * Sets the data encryption of the account - * @param encryptionKey The key to set - */ - setAccountDataEncryptionKey = proxySend( - this.kit, - this.contract.methods.setAccountDataEncryptionKey + getAttestationStat: ( + phoneNumber: string, + account: Address + ) => Promise = proxyCall( + this.contract.methods.getAttestationStats, + tupleParser(PhoneNumberUtils.getPhoneHash, stringIdentity), + (stat) => ({ completed: toNumber(stat[0]), total: toNumber(stat[1]) }) ) - /** - * Sets the metadataURL for the account - * @param url The url to set - */ - setMetadataURL = proxySend(this.kit, this.contract.methods.setMetadataURL) - - /** - * Sets the wallet address for the account - * @param address The address to set - */ - setWalletAddress = proxySend(this.kit, this.contract.methods.setWalletAddress) - /** * Calculates the amount of StableToken required to request Attestations * @param attestationsRequested The number of attestations to request @@ -156,9 +187,10 @@ export class AttestationsWrapper extends BaseWrapper { phoneNumber: string, account: Address ): Promise { + const accounts = await this.kit.contracts.getAccounts() const phoneHash = PhoneNumberUtils.getPhoneHash(phoneNumber) - const expirySeconds = await this.attestationExpirySeconds() - const nowInUnixSeconds = Math.floor(new Date().getTime() / 1000) + const expiryBlocks = await this.attestationExpiryBlocks() + const currentBlockNumber = await this.kit.web3.eth.getBlockNumber() const issuers = await this.contract.methods.getAttestationIssuers(phoneHash, account).call() const issuerState = Promise.all( @@ -172,18 +204,18 @@ export class AttestationsWrapper extends BaseWrapper { // Typechain is not properly typing getDataEncryptionKey const publicKeys: Promise = Promise.all( - issuers.map((issuer) => this.contract.methods.getDataEncryptionKey(issuer).call() as any) + issuers.map((issuer) => accounts.getDataEncryptionKey(issuer) as any) ) const isIncomplete = (status: AttestationState) => status === AttestationState.Incomplete - const hasNotExpired = (time: number) => nowInUnixSeconds < time + expirySeconds + const hasNotExpired = (blockNumber: number) => currentBlockNumber < blockNumber + expiryBlocks const isValidKey = (key: string) => key !== null && key !== '0x0' return zip3(issuers, await issuerState, await publicKeys) .filter( ([_issuer, attestation, publicKey]) => isIncomplete(attestation.attestationState) && - hasNotExpired(attestation.time) && + hasNotExpired(attestation.blockNumber) && isValidKey(publicKey) ) .map(([issuer, attestation, publicKey]) => ({ @@ -246,7 +278,7 @@ export class AttestationsWrapper extends BaseWrapper { }) ) return { - attestationExpirySeconds: await this.attestationExpirySeconds(), + attestationExpiryBlocks: await this.attestationExpiryBlocks(), attestationRequestFees: fees, } } @@ -308,15 +340,24 @@ export class AttestationsWrapper extends BaseWrapper { ) } + /** + * Selects the issuers for previously requested attestations for a phone number + * @param phoneNumber The phone number for which to request attestations for + * @param token The token with which to pay for the attestation fee + */ + async selectIssuers(phoneNumber: string) { + const phoneHash = PhoneNumberUtils.getPhoneHash(phoneNumber) + return toTransactionObject(this.kit, this.contract.methods.selectIssuers(phoneHash)) + } + /** * Reveals the phone number to the issuer of the attestation on-chain * @param phoneNumber The phone number which requested attestation * @param issuer The address of issuer of the attestation */ async reveal(phoneNumber: string, issuer: Address) { - const publicKey: string = (await this.contract.methods - .getDataEncryptionKey(issuer) - .call()) as any + const accounts = await this.kit.contracts.getAccounts() + const publicKey: string = (await accounts.getDataEncryptionKey(issuer)) as any if (!publicKey) { throw new Error('Issuer data encryption key is null') @@ -356,6 +397,9 @@ export class AttestationsWrapper extends BaseWrapper { const phoneHash = PhoneNumberUtils.getPhoneHash(phoneNumber) const expectedSourceMessage = attestationMessageToSign(phoneHash, account) const { r, s, v } = parseSignature(expectedSourceMessage, code, issuer.toLowerCase()) - return this.contract.methods.validateAttestationCode(phoneHash, account, v, r, s).call() + const result = await this.contract.methods + .validateAttestationCode(phoneHash, account, v, r, s) + .call() + return result.toLowerCase() !== NULL_ADDRESS } } diff --git a/packages/contractkit/src/wrappers/Election.ts b/packages/contractkit/src/wrappers/Election.ts index fbf35b6c87d..c18385fcbfb 100644 --- a/packages/contractkit/src/wrappers/Election.ts +++ b/packages/contractkit/src/wrappers/Election.ts @@ -80,6 +80,18 @@ export class ElectionWrapper extends BaseWrapper { toNumber ) + /** + * Returns the total votes for `group` made by `account`. + * @param group The address of the validator group. + * @param account The address of the voting account. + * @return The total votes for `group` made by `account`. + */ + getTotalVotesForGroup = proxyCall( + this.contract.methods.getTotalVotesForGroup, + undefined, + toBigNumber + ) + /** * Returns the groups that `account` has voted for. * @param account The address of the account casting votes. @@ -149,24 +161,6 @@ export class ElectionWrapper extends BaseWrapper { return zip((a, b) => ({ address: a, votes: new BigNumber(b), eligible: true }), res[0], res[1]) } - /** - * Marks a group eligible for electing validators. - * @param lesser The address of the group that has received fewer votes than this group. - * @param greater The address of the group that has received more votes than this group. - */ - async markGroupEligible(validatorGroup: Address): Promise> { - if (this.kit.defaultAccount == null) { - throw new Error(`missing kit.defaultAccount`) - } - - const value = toBigNumber( - await this.contract.methods.getTotalVotesForGroup(validatorGroup).call() - ) - const { lesser, greater } = await this.findLesserAndGreaterAfterVote(validatorGroup, value) - - return toTransactionObject(this.kit, this.contract.methods.markGroupEligible(lesser, greater)) - } - /** * Increments the number of total and pending votes for `group`. * @param validatorGroup The validator group to vote for. diff --git a/packages/contractkit/src/wrappers/LockedGold.ts b/packages/contractkit/src/wrappers/LockedGold.ts index bc3833f090c..49e9679b313 100644 --- a/packages/contractkit/src/wrappers/LockedGold.ts +++ b/packages/contractkit/src/wrappers/LockedGold.ts @@ -1,7 +1,5 @@ -import { eqAddress } from '@celo/utils/lib/address' import { zip } from '@celo/utils/lib/collections' import BigNumber from 'bignumber.js' -import Web3 from 'web3' import { Address } from '../base' import { LockedGold } from '../generated/types/LockedGold' import { @@ -12,7 +10,6 @@ import { proxyCall, proxySend, toBigNumber, - toTransactionObject, tupleParser, } from '../wrappers/BaseWrapper' @@ -28,10 +25,6 @@ interface AccountSummary { total: BigNumber nonvoting: BigNumber } - authorizations: { - voter: null | string - validator: null | string - } pendingWithdrawals: PendingWithdrawal[] } @@ -57,10 +50,7 @@ export class LockedGoldWrapper extends BaseWrapper { this.contract.methods.unlock, tupleParser(parseNumber) ) - /** - * Creates an account. - */ - createAccount = proxySend(this.kit, this.contract.methods.createAccount) + /** * Withdraws a gold that has been unlocked after the unlocking period has passed. * @param index The index of the pending withdrawal to withdraw. @@ -102,28 +92,7 @@ export class LockedGoldWrapper extends BaseWrapper { undefined, toBigNumber ) - /** - * Returns the voter for the specified account. - * @param account The address of the account. - * @return The address with which the account can vote. - */ - getVoterFromAccount: (account: string) => Promise
= proxyCall( - this.contract.methods.getVoterFromAccount - ) - /** - * Returns the validator for the specified account. - * @param account The address of the account. - * @return The address with which the account can register a validator or group. - */ - getValidatorFromAccount: (account: string) => Promise
= proxyCall( - this.contract.methods.getValidatorFromAccount - ) - /** - * Check if an account already exists. - * @param account The address of the account - * @return Returns `true` if account exists. Returns `false` otherwise. - */ - isAccount: (account: string) => Promise = proxyCall(this.contract.methods.isAccount) + /** * Returns current configuration parameters. */ @@ -136,54 +105,16 @@ export class LockedGoldWrapper extends BaseWrapper { async getAccountSummary(account: string): Promise { const nonvoting = await this.getAccountNonvotingLockedGold(account) const total = await this.getAccountTotalLockedGold(account) - const voter = await this.getVoterFromAccount(account) - const validator = await this.getValidatorFromAccount(account) const pendingWithdrawals = await this.getPendingWithdrawals(account) return { lockedGold: { total, nonvoting, }, - authorizations: { - voter: eqAddress(voter, account) ? null : voter, - validator: eqAddress(validator, account) ? null : validator, - }, pendingWithdrawals, } } - /** - * Authorize voting on behalf of this account to another address. - * @param account Address of the active account. - * @param voter Address to be used for voting. - * @return A CeloTransactionObject - */ - async authorizeVoter(account: Address, voter: Address): Promise> { - const sig = await this.getParsedSignatureOfAddress(account, voter) - // TODO(asa): Pass default tx "from" argument. - return toTransactionObject( - this.kit, - this.contract.methods.authorizeVoter(voter, sig.v, sig.r, sig.s) - ) - } - - /** - * Authorize validating on behalf of this account to another address. - * @param account Address of the active account. - * @param voter Address to be used for validating. - * @return A CeloTransactionObject - */ - async authorizeValidator( - account: Address, - validator: Address - ): Promise> { - const sig = await this.getParsedSignatureOfAddress(account, validator) - return toTransactionObject( - this.kit, - this.contract.methods.authorizeValidator(validator, sig.v, sig.r, sig.s) - ) - } - /** * Returns the pending withdrawals from unlocked gold for an account. * @param account The address of the account. @@ -199,14 +130,4 @@ export class LockedGoldWrapper extends BaseWrapper { withdrawals[0] ) } - - private async getParsedSignatureOfAddress(address: Address, signer: string) { - const hash = Web3.utils.soliditySha3({ type: 'address', value: address }) - const signature = (await this.kit.web3.eth.sign(hash, signer)).slice(2) - return { - r: `0x${signature.slice(0, 64)}`, - s: `0x${signature.slice(64, 128)}`, - v: Web3.utils.hexToNumber(signature.slice(128, 130)) + 27, - } - } } diff --git a/packages/contractkit/src/wrappers/SortedOracles.test.ts b/packages/contractkit/src/wrappers/SortedOracles.test.ts new file mode 100644 index 00000000000..cc9cbb26e9f --- /dev/null +++ b/packages/contractkit/src/wrappers/SortedOracles.test.ts @@ -0,0 +1,240 @@ +import { Address, CeloContract } from '../base' +import { newKitFromWeb3 } from '../kit' +import { NetworkConfig, testWithGanache } from '../test-utils/ganache-test' +import { OracleRate, SortedOraclesWrapper } from './SortedOracles' + +/* +TEST NOTES: +- In migrations: The only account that has cUSD is accounts[0] +*/ + +testWithGanache('SortedOracles Wrapper', (web3) => { + // NOTE: These values are set in test-utils/network-config.json, and are derived + // from the MNEMONIC. If the MNEMONIC has changed, these will need to be reset. + // To do that, look at the output of web3.eth.getAccounts(), and pick a few + // addresses from that set to be oracles + const stableTokenOracles: Address[] = NetworkConfig.stableToken.oracles + const oracleAddress = stableTokenOracles[stableTokenOracles.length - 1] + + const kit = newKitFromWeb3(web3) + let allAccounts: Address[] + let sortedOracles: SortedOraclesWrapper + let stableTokenAddress: Address + let nonOracleAddress: Address + + beforeAll(async () => { + sortedOracles = await kit.contracts.getSortedOracles() + stableTokenAddress = await kit.registry.addressFor(CeloContract.StableToken) + allAccounts = await web3.eth.getAccounts() + nonOracleAddress = allAccounts.find((addr) => { + return !stableTokenOracles.includes(addr) + })! + }) + + describe('#report', () => { + const numerator = 16 + const denominator = 1 + + describe('when reporting from a whitelisted Oracle', () => { + it('should be able to report a rate', async () => { + const initialRates: OracleRate[] = await sortedOracles.getRates(CeloContract.StableToken) + + const tx = await sortedOracles.report( + CeloContract.StableToken, + numerator, + denominator, + oracleAddress + ) + await tx.sendAndWaitForReceipt() + + const resultingRates: OracleRate[] = await sortedOracles.getRates(CeloContract.StableToken) + expect(resultingRates).not.toMatchObject(initialRates) + }) + + describe('when inserting into the middle of the existing rates', () => { + beforeEach(async () => { + const rates = [15, 20, 17] + for (let i = 0; i < stableTokenOracles.length - 1; i++) { + const tx = await sortedOracles.report( + CeloContract.StableToken, + rates[i], + denominator, + stableTokenOracles[i] + ) + await tx.sendAndWaitForReceipt() + } + }) + + const expectedLesserKey = stableTokenOracles[0] + const expectedGreaterKey = stableTokenOracles[2] + + const expectedOracleOrder = [ + stableTokenOracles[1], + stableTokenOracles[2], + oracleAddress, + stableTokenOracles[0], + ] + + it('passes the correct lesserKey and greaterKey as args', async () => { + const tx = await sortedOracles.report( + CeloContract.StableToken, + numerator, + denominator, + oracleAddress + ) + const actualArgs = tx.txo.arguments + expect(actualArgs[3]).toEqual(expectedLesserKey) + expect(actualArgs[4]).toEqual(expectedGreaterKey) + + await tx.sendAndWaitForReceipt() + }) + + it('inserts the new record in the right place', async () => { + const tx = await sortedOracles.report( + CeloContract.StableToken, + numerator, + denominator, + oracleAddress + ) + await tx.sendAndWaitForReceipt() + + const resultingRates: OracleRate[] = await sortedOracles.getRates( + CeloContract.StableToken + ) + + expect(resultingRates.map((r) => r.address)).toEqual(expectedOracleOrder) + }) + }) + }) + + describe('when reporting from a non-oracle address', () => { + it('should raise an error', async () => { + const tx = await sortedOracles.report( + CeloContract.StableToken, + numerator, + denominator, + nonOracleAddress + ) + await expect(tx.sendAndWaitForReceipt()).rejects.toThrow('sender was not an oracle') + }) + + it('should not change the list of rates', async () => { + const initialRates = await sortedOracles.getRates(CeloContract.StableToken) + try { + const tx = await sortedOracles.report( + CeloContract.StableToken, + numerator, + denominator, + nonOracleAddress + ) + await tx.sendAndWaitForReceipt() + } catch (err) { + // We don't need to do anything with this error other than catch it so + // it doesn't fail this test. + } finally { + const resultingRates = await sortedOracles.getRates(CeloContract.StableToken) + expect(resultingRates).toMatchObject(initialRates) + } + }) + }) + }) + + /** + * Proxy Calls to view methods + * + * The purpose of these tests is to verify that these wrapper functions exist, + * are calling the contract methods correctly, and get some value back. The + * values checked here are often dependent on setup occuring in the protocol + * migrations run in `yarn test:prepare`. If these tests are failing, the first + * thing to check is if there have been changes to the migrations + */ + describe('#getRates', () => { + beforeEach(async () => { + for (let i = 0; i < stableTokenOracles.length; i++) { + // reports these values: + // 1/2, 2/2, 3/2, 4/2 + // resulting in: 0.5, 1, 1.5, 2 + const tx = await sortedOracles.report( + CeloContract.StableToken, + i + 1, + 2, + stableTokenOracles[i] + ) + await tx.sendAndWaitForReceipt() + } + }) + it('SBAT getRates', async () => { + const rates = await sortedOracles.getRates(CeloContract.StableToken) + expect(rates.length).toBeGreaterThan(0) + for (const rate of rates) { + expect(rate).toHaveProperty('address') + expect(rate).toHaveProperty('rate') + expect(rate).toHaveProperty('medianRelation') + } + }) + + it('returns the rate as the result of the calculation numerator/denominator', async () => { + const expectedRates = ['2', '1.5', '1', '0.5'] + const response = await sortedOracles.getRates(CeloContract.StableToken) + const actualRates = response.map((r) => r.rate.toString()) + expect(actualRates).toEqual(expectedRates) + }) + }) + + describe('#isOracle', () => { + it('returns true when this address is a whitelisted oracle for this token', async () => { + expect(await sortedOracles.isOracle(CeloContract.StableToken, oracleAddress)).toEqual(true) + }) + it('returns false when this address is not an oracle', async () => { + expect(await sortedOracles.isOracle(CeloContract.StableToken, nonOracleAddress)).toEqual( + false + ) + }) + }) + + describe('#numRates', () => { + it('returns a count of rates reported for the specified token', async () => { + // Why 1? In packages/protocol/08_stabletoken, a single rate is reported + expect(await sortedOracles.numRates(CeloContract.StableToken)).toEqBigNumber(1) + }) + }) + + describe('#medianRate', () => { + it('returns the key for the median', async () => { + const returnedMedian = await sortedOracles.medianRate(CeloContract.StableToken) + // The value `10` comes from: packages/protocol/migrationsConfig.js: + // stableToken.goldPrice + expect(returnedMedian.rate).toEqBigNumber(10) + }) + }) + + describe('#reportExpirySeconds', () => { + it('returns the number of seconds after which a report expires', async () => { + const result = await sortedOracles.reportExpirySeconds() + expect(result).toEqBigNumber(3600) + }) + }) + + /** + * Helper Functions + * + * These are functions in the wrapper that call other functions, passing in + * some regularly used arguments. The purpose of these tests is to verify that + * those arguments are being set correctly. + */ + describe('getStableTokenRates', () => { + it('gets rates for Stable Token', async () => { + const usdRatesResult = await sortedOracles.getStableTokenRates() + const getRatesResult = await sortedOracles.getRates(CeloContract.StableToken) + expect(usdRatesResult).toEqual(getRatesResult) + }) + }) + + describe('reportStableToken', () => { + it('calls report with the address for StableToken', async () => { + const tx = await sortedOracles.reportStableToken(14, 1, oracleAddress) + await tx.sendAndWaitForReceipt() + expect(tx.txo.arguments[0]).toEqual(stableTokenAddress) + }) + }) +}) diff --git a/packages/contractkit/src/wrappers/SortedOracles.ts b/packages/contractkit/src/wrappers/SortedOracles.ts index 7da98f72697..56657d08248 100644 --- a/packages/contractkit/src/wrappers/SortedOracles.ts +++ b/packages/contractkit/src/wrappers/SortedOracles.ts @@ -1,21 +1,124 @@ +import { eqAddress } from '@celo/utils/lib/address' import BigNumber from 'bignumber.js' +import { Address, CeloContract, CeloToken, NULL_ADDRESS } from '../base' import { SortedOracles } from '../generated/types/SortedOracles' -import { BaseWrapper, proxyCall, toBigNumber } from './BaseWrapper' +import { + BaseWrapper, + CeloTransactionObject, + proxyCall, + toBigNumber, + toTransactionObject, +} from './BaseWrapper' + +export enum MedianRelation { + Undefined, + Lesser, + Greater, + Equal, +} export interface SortedOraclesConfig { reportExpirySeconds: BigNumber } +export interface OracleRate { + address: Address + rate: BigNumber + medianRelation: MedianRelation +} + +export interface MedianRate { + rate: BigNumber +} + /** * Currency price oracle contract. */ export class SortedOraclesWrapper extends BaseWrapper { + /** + * Gets the number of rates that have been reported for the given token + * @param token The CeloToken token for which the Celo Gold exchange rate is being reported. + * @return The number of reported oracle rates for `token`. + */ + async numRates(token: CeloToken): Promise { + const tokenAddress = await this.kit.registry.addressFor(token) + const response = await this.contract.methods.numRates(tokenAddress).call() + return toBigNumber(response).toNumber() + } + + /** + * Returns the median rate for the given token + * @param token The CeloToken token for which the Celo Gold exchange rate is being reported. + * @return The median exchange rate for `token`, expressed as: + * amount of that token / equivalent amount in Celo Gold + */ + async medianRate(token: CeloToken): Promise { + const tokenAddress = await this.kit.registry.addressFor(token) + const response = await this.contract.methods.medianRate(tokenAddress).call() + return { + rate: toBigNumber(response[0]).div(toBigNumber(response[1])), + } + } + + /** + * Checks if the given address is whitelisted as an oracle for the token + * @param token The CeloToken token + * @param oracle The address that we're checking the oracle status of + * @returns boolean describing whether this account is an oracle + */ + async isOracle(token: CeloToken, oracle: Address): Promise { + const tokenAddress = await this.kit.registry.addressFor(token) + return this.contract.methods.isOracle(tokenAddress, oracle).call() + } + /** * Returns the report expiry parameter. * @returns Current report expiry. */ reportExpirySeconds = proxyCall(this.contract.methods.reportExpirySeconds, undefined, toBigNumber) + /** + * Updates an oracle value and the median. + * @param token The address of the token for which the Celo Gold exchange rate is being reported. + * @param numerator The amount of tokens equal to `denominator` Celo Gold. + * @param denominator The amount of Celo Gold that the `numerator` tokens are equal to. + */ + async report( + token: CeloToken, + numerator: number, + denominator: number, + oracleAddress: Address + ): Promise> { + const tokenAddress = await this.kit.registry.addressFor(token) + + const { lesserKey, greaterKey } = await this.findLesserAndGreaterKeys( + token, + numerator, + denominator, + oracleAddress + ) + + return toTransactionObject( + this.kit, + this.contract.methods.report(tokenAddress, numerator, denominator, lesserKey, greaterKey), + { from: oracleAddress } + ) + } + + /** + * Updates an oracle value and the median. + * @param token The address of the token for which the Celo Gold exchange rate is being reported. + * @param numerator The amount of tokens equal to `denominator` Celo Gold. + * @param denominator The amount of Celo Gold that the `numerator` tokens are equal to. + */ + async reportStableToken( + numerator: number, + denominator: number, + oracleAddress: Address + ): Promise> { + return this.report(CeloContract.StableToken, numerator, denominator, oracleAddress) + } + /** * Returns current configuration parameters. */ @@ -24,4 +127,68 @@ export class SortedOraclesWrapper extends BaseWrapper { reportExpirySeconds: await this.reportExpirySeconds(), } } + + /** + * Helper function to get the rates for StableToken, by passing the address + * of StableToken to `getRates`. + */ + getStableTokenRates = async (): Promise => this.getRates(CeloContract.StableToken) + + /** + * Gets all elements from the doubly linked list. + * @param token The CeloToken representing the token for which the Celo + * Gold exchange rate is being reported. Example: CeloContract.StableToken + * @return An unpacked list of elements from largest to smallest. + */ + async getRates(token: CeloToken): Promise { + const tokenAddress = await this.kit.registry.addressFor(token) + const response = await this.contract.methods.getRates(tokenAddress).call() + const rates: OracleRate[] = [] + const denominator = await this.getInternalDenominator() + + for (let i = 0; i < response[0].length; i++) { + const medRelIndex = parseInt(response[2][i], 10) + rates.push({ + address: response[0][i], + rate: toBigNumber(response[1][i]).div(denominator), + medianRelation: medRelIndex, + }) + } + return rates + } + + private async getInternalDenominator(): Promise { + return toBigNumber(await this.contract.methods.DENOMINATOR().call()) + } + + private async findLesserAndGreaterKeys( + token: CeloToken, + numerator: number, + denominator: number, + oracleAddress: Address + ): Promise<{ lesserKey: Address; greaterKey: Address }> { + const currentRates: OracleRate[] = await this.getRates(token) + + // This is how the contract calculates the rate from the numerator and denominator. + // To figure out where this new report goes in the list, we need to compare this + // value with the other rates + const value = toBigNumber(numerator.toString()).div(toBigNumber(denominator.toString())) + + let greaterKey = NULL_ADDRESS + let lesserKey = NULL_ADDRESS + + // This leverages the fact that the currentRates are already sorted from + // greatest to lowest value + for (const rate of currentRates) { + if (!eqAddress(rate.address, oracleAddress)) { + if (rate.rate.isLessThanOrEqualTo(value)) { + lesserKey = rate.address + break + } + greaterKey = rate.address + } + } + + return { lesserKey, greaterKey } + } } diff --git a/packages/contractkit/src/wrappers/StableTokenWrapper.ts b/packages/contractkit/src/wrappers/StableTokenWrapper.ts index e8593fe24df..558d55b539d 100644 --- a/packages/contractkit/src/wrappers/StableTokenWrapper.ts +++ b/packages/contractkit/src/wrappers/StableTokenWrapper.ts @@ -71,7 +71,6 @@ export class StableTokenWrapper extends BaseWrapper { toBigNumber ) - minter = proxyCall(this.contract.methods.minter) owner = proxyCall(this.contract.methods.owner) /** diff --git a/packages/contractkit/src/wrappers/Validators.test.ts b/packages/contractkit/src/wrappers/Validators.test.ts index 1d54ed80bf8..2ecbbce1fe2 100644 --- a/packages/contractkit/src/wrappers/Validators.test.ts +++ b/packages/contractkit/src/wrappers/Validators.test.ts @@ -2,6 +2,7 @@ import BigNumber from 'bignumber.js' import Web3 from 'web3' import { newKitFromWeb3 } from '../kit' import { testWithGanache } from '../test-utils/ganache-test' +import { AccountsWrapper } from './Accounts' import { LockedGoldWrapper } from './LockedGold' import { ValidatorsWrapper } from './Validators' @@ -25,12 +26,13 @@ const publicKeysData = '0x' + publicKey + blsPublicKey + blsPoP testWithGanache('Validators Wrapper', (web3) => { const kit = newKitFromWeb3(web3) let accounts: string[] = [] + let accountsInstance: AccountsWrapper let validators: ValidatorsWrapper let lockedGold: LockedGoldWrapper const registerAccountWithLockedGold = async (account: string) => { - if (!(await lockedGold.isAccount(account))) { - await lockedGold.createAccount().sendAndWaitForReceipt({ from: account }) + if (!(await accountsInstance.isAccount(account))) { + await accountsInstance.createAccount().sendAndWaitForReceipt({ from: account }) } await lockedGold.lock().sendAndWaitForReceipt({ from: account, value: minLockedGoldValue }) } @@ -39,15 +41,14 @@ testWithGanache('Validators Wrapper', (web3) => { accounts = await web3.eth.getAccounts() validators = await kit.contracts.getValidators() lockedGold = await kit.contracts.getLockedGold() + accountsInstance = await kit.contracts.getAccounts() }) const setupGroup = async (groupAccount: string) => { await registerAccountWithLockedGold(groupAccount) - await (await validators.registerValidatorGroup( - 'The Group', - 'thegroup.com', - new BigNumber(0.1) - )).sendAndWaitForReceipt({ from: groupAccount }) + await (await validators.registerValidatorGroup(new BigNumber(0.1))).sendAndWaitForReceipt({ + from: groupAccount, + }) } const setupValidator = async (validatorAccount: string) => { @@ -55,8 +56,6 @@ testWithGanache('Validators Wrapper', (web3) => { // set account1 as the validator await validators .registerValidator( - 'Good old validator', - 'goodold.com', // @ts-ignore publicKeysData ) @@ -81,7 +80,7 @@ testWithGanache('Validators Wrapper', (web3) => { await setupGroup(groupAccount) await setupValidator(validatorAccount) await validators.affiliate(groupAccount).sendAndWaitForReceipt({ from: validatorAccount }) - await validators.addMember(validatorAccount).sendAndWaitForReceipt({ from: groupAccount }) + await (await validators.addMember(groupAccount, validatorAccount)).sendAndWaitForReceipt() const members = await validators.getValidatorGroup(groupAccount).then((group) => group.members) expect(members).toContain(validatorAccount) @@ -100,7 +99,7 @@ testWithGanache('Validators Wrapper', (web3) => { for (const validator of [validator1, validator2]) { await setupValidator(validator) await validators.affiliate(groupAccount).sendAndWaitForReceipt({ from: validator }) - await validators.addMember(validator).sendAndWaitForReceipt({ from: groupAccount }) + await (await validators.addMember(groupAccount, validator)).sendAndWaitForReceipt() } const members = await validators diff --git a/packages/contractkit/src/wrappers/Validators.ts b/packages/contractkit/src/wrappers/Validators.ts index 754bedcadc2..d2f3c303b97 100644 --- a/packages/contractkit/src/wrappers/Validators.ts +++ b/packages/contractkit/src/wrappers/Validators.ts @@ -13,74 +13,80 @@ import { export interface Validator { address: Address - name: string - url: string publicKey: string affiliation: string | null + score: BigNumber } export interface ValidatorGroup { address: Address - name: string - url: string members: Address[] commission: BigNumber } -export interface BalanceRequirements { - group: BigNumber - validator: BigNumber -} - -export interface DeregistrationLockups { - group: BigNumber - validator: BigNumber +export interface LockedGoldRequirements { + value: BigNumber + duration: BigNumber } export interface ValidatorsConfig { - balanceRequirements: BalanceRequirements - deregistrationLockups: DeregistrationLockups + groupLockedGoldRequirements: LockedGoldRequirements + validatorLockedGoldRequirements: LockedGoldRequirements maxGroupSize: BigNumber } /** * Contract for voting for validators and managing validator groups. */ +// TODO(asa): Support authorized validators export class ValidatorsWrapper extends BaseWrapper { affiliate = proxySend(this.kit, this.contract.methods.affiliate) deaffiliate = proxySend(this.kit, this.contract.methods.deaffiliate) + removeMember = proxySend(this.kit, this.contract.methods.removeMember) registerValidator = proxySend(this.kit, this.contract.methods.registerValidator) - async registerValidatorGroup( - name: string, - url: string, - commission: BigNumber - ): Promise> { + async registerValidatorGroup(commission: BigNumber): Promise> { return toTransactionObject( this.kit, - this.contract.methods.registerValidatorGroup(name, url, toFixed(commission).toFixed()) + this.contract.methods.registerValidatorGroup(toFixed(commission).toFixed()) ) } + async addMember(group: string, member: string): Promise> { + const numMembers = await this.getGroupNumMembers(group) + if (numMembers.isZero()) { + const election = await this.kit.contracts.getElection() + const voteWeight = await election.getTotalVotesForGroup(group) + const { lesser, greater } = await election.findLesserAndGreaterAfterVote(group, voteWeight) + + return toTransactionObject( + this.kit, + this.contract.methods.addFirstMember(member, lesser, greater), + { from: group } + ) + } else { + return toTransactionObject(this.kit, this.contract.methods.addMember(member), { from: group }) + } + } /** - * Returns the current registration requirements. - * @returns Group and validator registration requirements. + * Returns the Locked Gold requirements for validators. + * @returns The Locked Gold requirements for validators. */ - async getBalanceRequirements(): Promise { - const res = await this.contract.methods.getBalanceRequirements().call() + async getValidatorLockedGoldRequirements(): Promise { + const res = await this.contract.methods.getValidatorLockedGoldRequirements().call() return { - group: toBigNumber(res[0]), - validator: toBigNumber(res[1]), + value: toBigNumber(res[0]), + duration: toBigNumber(res[1]), } } /** - * Returns the lockup periods after deregistering groups and validators. - * @return The lockup periods after deregistering groups and validators. + * Returns the Locked Gold requirements for validator groups. + * @returns The Locked Gold requirements for validator groups. */ - async getDeregistrationLockups(): Promise { - const res = await this.contract.methods.getDeregistrationLockups().call() + async getGroupLockedGoldRequirements(): Promise { + const res = await this.contract.methods.getGroupLockedGoldRequirements().call() return { - group: toBigNumber(res[0]), - validator: toBigNumber(res[1]), + value: toBigNumber(res[0]), + duration: toBigNumber(res[1]), } } @@ -89,13 +95,13 @@ export class ValidatorsWrapper extends BaseWrapper { */ async getConfig(): Promise { const res = await Promise.all([ - this.getBalanceRequirements(), - this.getDeregistrationLockups(), + this.getValidatorLockedGoldRequirements(), + this.getGroupLockedGoldRequirements(), this.contract.methods.maxGroupSize().call(), ]) return { - balanceRequirements: res[0], - deregistrationLockups: res[1], + validatorLockedGoldRequirements: res[0], + groupLockedGoldRequirements: res[1], maxGroupSize: toBigNumber(res[2]), } } @@ -116,10 +122,9 @@ export class ValidatorsWrapper extends BaseWrapper { const res = await this.contract.methods.getValidator(address).call() return { address, - name: res[0], - url: res[1], - publicKey: res[2] as any, - affiliation: res[3], + publicKey: res[0] as any, + affiliation: res[1], + score: fromFixed(new BigNumber(res[2])), } } @@ -137,9 +142,6 @@ export class ValidatorsWrapper extends BaseWrapper { */ isValidatorGroup = proxyCall(this.contract.methods.isValidatorGroup) - addMember = proxySend(this.kit, this.contract.methods.addMember) - removeMember = proxySend(this.kit, this.contract.methods.removeMember) - async reorderMember(groupAddr: Address, validator: Address, newIndex: number) { const group = await this.getValidatorGroup(groupAddr) @@ -179,10 +181,8 @@ export class ValidatorsWrapper extends BaseWrapper { const res = await this.contract.methods.getValidatorGroup(address).call() return { address, - name: res[0], - url: res[1], - members: res[2], - commission: fromFixed(new BigNumber(res[3])), + members: res[0], + commission: fromFixed(new BigNumber(res[1])), } } } diff --git a/packages/dappkit/README.md b/packages/dappkit/README.md index 0b679bfd7c6..e54b0b2df95 100644 --- a/packages/dappkit/README.md +++ b/packages/dappkit/README.md @@ -55,7 +55,7 @@ Once you have the account address, you can make calls against your own smart con const address = dappkitResponse.address this.setState({ address, phoneNumber: dappkitResponse.phoneNumber, isLoadingBalance: true }) - const kit = newKit('https://alfajores-infura.celo-testnet.org') + const kit = newKit('https://alfajores-forno.celo-testnet.org') kit.defaultAccount = address const stableToken = await kit.contracts.getStableToken() diff --git a/packages/docs/command-line-interface/account.md b/packages/docs/command-line-interface/account.md index 693d99dda8d..cde09896e6c 100644 --- a/packages/docs/command-line-interface/account.md +++ b/packages/docs/command-line-interface/account.md @@ -4,6 +4,26 @@ description: Manage your account, send and receive Celo Gold and Celo Dollars ## Commands +### Authorize + +Authorize an attestation, validation or vote signing key + +``` +USAGE + $ celocli account:authorize + +OPTIONS + -r, --role=vote|validation|attestation Role to delegate + --from=0xc1912fEE45d61C87Cc5EA59DaE31190FFFFf232d (required) Account Address + --to=0xc1912fEE45d61C87Cc5EA59DaE31190FFFFf232d (required) Account Address + +EXAMPLE + authorize --from 0x5409ED021D9299bf6814279A6A1411A7e866A631 --role vote --to + 0xc1912fEE45d61C87Cc5EA59DaE31190FFFFf232d +``` + +_See code: [packages/cli/src/commands/account/authorize.ts](https://github.com/celo-org/celo-monorepo/tree/master/packages/cli/src/commands/account/authorize.ts)_ + ### Balance View Celo Dollar and Gold balances given account address @@ -18,6 +38,100 @@ EXAMPLE _See code: [packages/cli/src/commands/account/balance.ts](https://github.com/celo-org/celo-monorepo/tree/master/packages/cli/src/commands/account/balance.ts)_ +### Change-attestation-service-url + +Change the URL of the attestation service in a local metadata file + +``` +USAGE + $ celocli account:change-attestation-service-url FILE + +ARGUMENTS + FILE Path of the metadata file + +OPTIONS + --url=htttps://www.celo.org (required) The url you want to claim + +EXAMPLE + change-attestation-service-url ~/metadata.json +``` + +_See code: [packages/cli/src/commands/account/change-attestation-service-url.ts](https://github.com/celo-org/celo-monorepo/tree/master/packages/cli/src/commands/account/change-attestation-service-url.ts)_ + +### Change-domain + +Change the domain in a local metadata file + +``` +USAGE + $ celocli account:change-domain FILE + +ARGUMENTS + FILE Path of the metadata file + +OPTIONS + --domain=domain (required) The domain you want to claim + +EXAMPLE + change-domain ~/metadata.json +``` + +_See code: [packages/cli/src/commands/account/change-domain.ts](https://github.com/celo-org/celo-monorepo/tree/master/packages/cli/src/commands/account/change-domain.ts)_ + +### Change-name + +Change the name in a local metadata file + +``` +USAGE + $ celocli account:change-name FILE + +ARGUMENTS + FILE Path of the metadata file + +OPTIONS + --name=name (required) The name you want to claim + +EXAMPLE + change-name ~/metadata.json +``` + +_See code: [packages/cli/src/commands/account/change-name.ts](https://github.com/celo-org/celo-monorepo/tree/master/packages/cli/src/commands/account/change-name.ts)_ + +### Create-metadata + +Create an empty metadata file + +``` +USAGE + $ celocli account:create-metadata FILE + +ARGUMENTS + FILE Path where the metadata should be saved + +EXAMPLE + create-metadata ~/metadata.json +``` + +_See code: [packages/cli/src/commands/account/create-metadata.ts](https://github.com/celo-org/celo-monorepo/tree/master/packages/cli/src/commands/account/create-metadata.ts)_ + +### Get-metadata + +Show information about an address + +``` +USAGE + $ celocli account:get-metadata ADDRESS + +ARGUMENTS + ADDRESS Address to get metadata for + +EXAMPLE + get-metadata 0x97f7333c51897469E8D98E7af8653aAb468050a3 +``` + +_See code: [packages/cli/src/commands/account/get-metadata.ts](https://github.com/celo-org/celo-monorepo/tree/master/packages/cli/src/commands/account/get-metadata.ts)_ + ### Isvalidator Check whether a given address is elected to be validating in the current epoch @@ -46,6 +160,59 @@ EXAMPLE _See code: [packages/cli/src/commands/account/new.ts](https://github.com/celo-org/celo-monorepo/tree/master/packages/cli/src/commands/account/new.ts)_ +### Register + +Register an account + +``` +USAGE + $ celocli account:register + +OPTIONS + --from=0xc1912fEE45d61C87Cc5EA59DaE31190FFFFf232d (required) Account Address + --name=name (required) + +EXAMPLE + register +``` + +_See code: [packages/cli/src/commands/account/register.ts](https://github.com/celo-org/celo-monorepo/tree/master/packages/cli/src/commands/account/register.ts)_ + +### Register-metadata + +Register metadata about an address + +``` +USAGE + $ celocli account:register-metadata + +OPTIONS + --from=0xc1912fEE45d61C87Cc5EA59DaE31190FFFFf232d (required) Addess of the account to set metadata for + --url=htttps://www.celo.org (required) The url to the metadata you want to register + +EXAMPLE + register-metadata --url https://www.celo.org --from 0x0 +``` + +_See code: [packages/cli/src/commands/account/register-metadata.ts](https://github.com/celo-org/celo-monorepo/tree/master/packages/cli/src/commands/account/register-metadata.ts)_ + +### Show-metadata + +Show the data in a local metadata file + +``` +USAGE + $ celocli account:show-metadata FILE + +ARGUMENTS + FILE Path of the metadata file + +EXAMPLE + show-metadata ~/metadata.json +``` + +_See code: [packages/cli/src/commands/account/show-metadata.ts](https://github.com/celo-org/celo-monorepo/tree/master/packages/cli/src/commands/account/show-metadata.ts)_ + ### Transferdollar Transfer Celo Dollars diff --git a/packages/docs/command-line-interface/identity.md b/packages/docs/command-line-interface/identity.md deleted file mode 100644 index 07b85c8591d..00000000000 --- a/packages/docs/command-line-interface/identity.md +++ /dev/null @@ -1,134 +0,0 @@ ---- -description: Change the URL of the attestation service in a local metadata file ---- - -## Commands - -### Change-attestation-service-url - -Change the URL of the attestation service in a local metadata file - -``` -USAGE - $ celocli identity:change-attestation-service-url FILE - -ARGUMENTS - FILE Path of the metadata file - -OPTIONS - --url=htttps://www.celo.org (required) The url you want to claim - -EXAMPLE - change-attestation-service-url ~/metadata.json -``` - -_See code: [packages/cli/src/commands/identity/change-attestation-service-url.ts](https://github.com/celo-org/celo-monorepo/tree/master/packages/cli/src/commands/identity/change-attestation-service-url.ts)_ - -### Change-domain - -Change the domain in a local metadata file - -``` -USAGE - $ celocli identity:change-domain FILE - -ARGUMENTS - FILE Path of the metadata file - -OPTIONS - --domain=domain (required) The domain you want to claim - -EXAMPLE - change-domain ~/metadata.json -``` - -_See code: [packages/cli/src/commands/identity/change-domain.ts](https://github.com/celo-org/celo-monorepo/tree/master/packages/cli/src/commands/identity/change-domain.ts)_ - -### Change-name - -Change the name in a local metadata file - -``` -USAGE - $ celocli identity:change-name FILE - -ARGUMENTS - FILE Path of the metadata file - -OPTIONS - --name=name (required) The name you want to claim - -EXAMPLE - change-name ~/metadata.json -``` - -_See code: [packages/cli/src/commands/identity/change-name.ts](https://github.com/celo-org/celo-monorepo/tree/master/packages/cli/src/commands/identity/change-name.ts)_ - -### Create-metadata - -Create an empty metadata file - -``` -USAGE - $ celocli identity:create-metadata FILE - -ARGUMENTS - FILE Path where the metadata should be saved - -EXAMPLE - create-metadata ~/metadata.json -``` - -_See code: [packages/cli/src/commands/identity/create-metadata.ts](https://github.com/celo-org/celo-monorepo/tree/master/packages/cli/src/commands/identity/create-metadata.ts)_ - -### Get-metadata - -Show information about an address - -``` -USAGE - $ celocli identity:get-metadata ADDRESS - -ARGUMENTS - ADDRESS Address to get metadata for - -EXAMPLE - get-metadata 0x97f7333c51897469E8D98E7af8653aAb468050a3 -``` - -_See code: [packages/cli/src/commands/identity/get-metadata.ts](https://github.com/celo-org/celo-monorepo/tree/master/packages/cli/src/commands/identity/get-metadata.ts)_ - -### Register-metadata - -Register metadata about an address - -``` -USAGE - $ celocli identity:register-metadata - -OPTIONS - --from=0xc1912fEE45d61C87Cc5EA59DaE31190FFFFf232d (required) Addess of the account to set metadata for - --url=htttps://www.celo.org (required) The url to the metadata you want to register - -EXAMPLE - register-metadata --url https://www.celo.org --from 0x0 -``` - -_See code: [packages/cli/src/commands/identity/register-metadata.ts](https://github.com/celo-org/celo-monorepo/tree/master/packages/cli/src/commands/identity/register-metadata.ts)_ - -### Show-metadata - -Show the data in a local metadata file - -``` -USAGE - $ celocli identity:show-metadata FILE - -ARGUMENTS - FILE Path of the metadata file - -EXAMPLE - show-metadata ~/metadata.json -``` - -_See code: [packages/cli/src/commands/identity/show-metadata.ts](https://github.com/celo-org/celo-monorepo/tree/master/packages/cli/src/commands/identity/show-metadata.ts)_ diff --git a/packages/docs/command-line-interface/lockedgold.md b/packages/docs/command-line-interface/lockedgold.md index ef067a6e750..949255f3f15 100644 --- a/packages/docs/command-line-interface/lockedgold.md +++ b/packages/docs/command-line-interface/lockedgold.md @@ -4,26 +4,6 @@ description: View and manage locked Celo Gold ## Commands -### Authorize - -Authorize validating or voting address for a Locked Gold account - -``` -USAGE - $ celocli lockedgold:authorize - -OPTIONS - -r, --role=voter|validator Role to delegate - --from=0xc1912fEE45d61C87Cc5EA59DaE31190FFFFf232d (required) Account Address - --to=0xc1912fEE45d61C87Cc5EA59DaE31190FFFFf232d (required) Account Address - -EXAMPLE - authorize --from 0x5409ED021D9299bf6814279A6A1411A7e866A631 --role voter --to - 0xc1912fEE45d61C87Cc5EA59DaE31190FFFFf232d -``` - -_See code: [packages/cli/src/commands/lockedgold/authorize.ts](https://github.com/celo-org/celo-monorepo/tree/master/packages/cli/src/commands/lockedgold/authorize.ts)_ - ### Lock Locks Celo Gold to be used in governance and validator elections. @@ -42,23 +22,6 @@ EXAMPLE _See code: [packages/cli/src/commands/lockedgold/lock.ts](https://github.com/celo-org/celo-monorepo/tree/master/packages/cli/src/commands/lockedgold/lock.ts)_ -### Register - -Register an account for Locked Gold - -``` -USAGE - $ celocli lockedgold:register - -OPTIONS - --from=0xc1912fEE45d61C87Cc5EA59DaE31190FFFFf232d (required) Account Address - -EXAMPLE - register -``` - -_See code: [packages/cli/src/commands/lockedgold/register.ts](https://github.com/celo-org/celo-monorepo/tree/master/packages/cli/src/commands/lockedgold/register.ts)_ - ### Show Show Locked Gold information for a given account diff --git a/packages/docs/command-line-interface/validator.md b/packages/docs/command-line-interface/validator.md index 4c72974b8fd..266299fc06b 100644 --- a/packages/docs/command-line-interface/validator.md +++ b/packages/docs/command-line-interface/validator.md @@ -48,12 +48,10 @@ USAGE OPTIONS --from=0xc1912fEE45d61C87Cc5EA59DaE31190FFFFf232d (required) Address for the Validator - --name=name (required) --publicKey=0x (required) Public Key - --url=url (required) EXAMPLE - register --from 0x47e172F6CfB6c7D01C1574fa3E2Be7CC73269D95 --name myName --url "http://validator.com" --publicKey + register --from 0x47e172F6CfB6c7D01C1574fa3E2Be7CC73269D95 --publicKey 0xc52f3fab06e22a54915a8765c4f6826090cfac5e40282b43844bf1c0df83aaa632e55b67869758f2291d1aabe0ebecc7cbf4236aaa45e3e0cfbf 997eda082ae19d3e1d8f49f6b0d8e9a03d80ca07b1d24cf1cc0557bdcc04f5e17a46e35d02d0d411d956dbd5d2d2464eebd7b74ae30005d223780d 785d2abc5644fac7ac29fb0e302bdc80c81a5d45018b68b1045068a4b3a4861c93037685fd0d252d7405011220a66a6257562d0c26dabf64485a1d diff --git a/packages/docs/command-line-interface/validatorgroup.md b/packages/docs/command-line-interface/validatorgroup.md index 291c735fc92..2611168b7a1 100644 --- a/packages/docs/command-line-interface/validatorgroup.md +++ b/packages/docs/command-line-interface/validatorgroup.md @@ -54,11 +54,9 @@ USAGE OPTIONS --commission=commission (required) --from=0xc1912fEE45d61C87Cc5EA59DaE31190FFFFf232d (required) Address for the Validator Group - --name=name (required) - --url=url (required) EXAMPLE - register --from 0x47e172F6CfB6c7D01C1574fa3E2Be7CC73269D95 --name myName --url "http://vgroup.com" --commission 0.1 + register --from 0x47e172F6CfB6c7D01C1574fa3E2Be7CC73269D95 --name myName --commission 0.1 ``` _See code: [packages/cli/src/commands/validatorgroup/register.ts](https://github.com/celo-org/celo-monorepo/tree/master/packages/cli/src/commands/validatorgroup/register.ts)_ diff --git a/packages/docs/developer-resources/contractkit/setup.md b/packages/docs/developer-resources/contractkit/setup.md index 7a0c68e1a8d..afdbf88d4d7 100644 --- a/packages/docs/developer-resources/contractkit/setup.md +++ b/packages/docs/developer-resources/contractkit/setup.md @@ -19,7 +19,7 @@ To start working with contractkit you need a `kit` instance: ```ts import { newKit } from '@celo/contractkit' -const kit = newKit('https://alfajores-infura.celo-testnet.org') +const kit = newKit('https://alfajores-forno.celo-testnet.org') ``` To access web3: diff --git a/packages/docs/developer-resources/dappkit/usage.md b/packages/docs/developer-resources/dappkit/usage.md index e0251cdd7d8..3943b1eb3a3 100644 --- a/packages/docs/developer-resources/dappkit/usage.md +++ b/packages/docs/developer-resources/dappkit/usage.md @@ -43,7 +43,7 @@ Once you have the account address, you can make calls against your own smart con const address = dappkitResponse.address this.setState({ address, phoneNumber: dappkitResponse.phoneNumber, isLoadingBalance: true }) - const kit = newKit('https://alfajores-infura.celo-testnet.org') + const kit = newKit('https://alfajores-forno.celo-testnet.org') kit.defaultAccount = address const stableToken = await kit.contracts.getStableToken() diff --git a/packages/docs/getting-started/running-a-validator.md b/packages/docs/getting-started/running-a-validator.md index 207442ff4f8..e2dca1cc105 100644 --- a/packages/docs/getting-started/running-a-validator.md +++ b/packages/docs/getting-started/running-a-validator.md @@ -27,7 +27,7 @@ If you are starting up a validator, please consider leaving it running for a few Some users have reported issues using the most recent version of node. Use the LTS for greater reliability. {% hint style="info" %} -A note about conventions: +A note about conventions: The code you'll see on this page is bash commands and their output. A $ signifies the bash prompt. Everything following it is the command you should run in a terminal. The $ isn't part of the command, so don't copy it. @@ -119,8 +119,8 @@ $ celocli account:unlock --account $CELO_VALIDATOR_ADDRESS --password +$ celocli account:register --from $CELO_VALIDATOR_ADDRESS --name ``` Make a locked Gold commitment for both accounts in order to secure the right to register a validator and validator group. The current requirement is 1 Celo Gold with a notice period of 60 days. If you choose to stake more gold, or a longer notice period, be sure to use those values below: @@ -134,11 +134,11 @@ $ celocli lockedgold:lockup --from $CELO_VALIDATOR_ADDRESS --goldAmount 10000000 Register your validator group: -`$ celocli validatorgroup:register --id --name --url --from $CELO_VALIDATOR_GROUP_ADDRESS --noticePeriod 5184000` +`$ celocli validatorgroup:register --id --from $CELO_VALIDATOR_GROUP_ADDRESS --noticePeriod 5184000` Register your validator: -`` $ celocli validator:register --id --name --url --from $CELO_VALIDATOR_ADDRESS --noticePeriod 5184000 --publicKey 0x`openssl rand -hex 64`$CELO_VALIDATOR_POP `` +`` $ celocli validator:register --id --from $CELO_VALIDATOR_ADDRESS --noticePeriod 5184000 --publicKey 0x`openssl rand -hex 64`$CELO_VALIDATOR_POP `` {% hint style="info" %} **Roadmap**: Note that the “publicKey” first part of the public key field is currently ignored, and thus can be set to any 128 character hex value. The rest is used for the BLS public key and proof-of-possession. diff --git a/packages/helm-charts/attestation-service/templates/attestation.statefulset.yaml b/packages/helm-charts/attestation-service/templates/attestation.statefulset.yaml index 96e65bbb707..75195d6cbb0 100644 --- a/packages/helm-charts/attestation-service/templates/attestation.statefulset.yaml +++ b/packages/helm-charts/attestation-service/templates/attestation.statefulset.yaml @@ -67,7 +67,7 @@ spec: - name: DB_URL value: sqlite://db/dev.db - name: CELO_PROVIDER - value: https://{{ .Release.Namespace }}-infura.{{ .Values.domain.name }}.org + value: https://{{ .Release.Namespace }}-forno.{{ .Values.domain.name }}.org - name: APP_SIGNATURE value: {{ .Values.attestation_service.sms_retriever_hash_code }}} - name: NEXMO_KEY diff --git a/packages/helm-charts/blockscout/Chart.yaml b/packages/helm-charts/blockscout/Chart.yaml index 0e50daced27..4d7e5860909 100644 --- a/packages/helm-charts/blockscout/Chart.yaml +++ b/packages/helm-charts/blockscout/Chart.yaml @@ -1,8 +1,8 @@ name: blockscout -version: 0.0.1 +version: 0.0.2 description: Chart which is used to deploy a blockscout setup for a celo testnet keywords: - ethereum - blockchain - blockscout -appVersion: v1.7.3 +appVersion: v2.0.4-beta diff --git a/packages/helm-charts/blockscout/templates/_helpers.tpl b/packages/helm-charts/blockscout/templates/_helpers.tpl index dbfaffba265..766228f4716 100644 --- a/packages/helm-charts/blockscout/templates/_helpers.tpl +++ b/packages/helm-charts/blockscout/templates/_helpers.tpl @@ -18,16 +18,16 @@ volumes: {{- end -}} {{- define "celo.blockscout-env-vars" -}} -- name: DB_USERNAME +- name: DATABASE_USER valueFrom: secretKeyRef: name: {{ .Release.Namespace }}-blockscout - key: DB_USERNAME -- name: DB_PASSWORD + key: DATABASE_USER +- name: DATABASE_PASSWORD valueFrom: secretKeyRef: name: {{ .Release.Namespace }}-blockscout - key: DB_PASSWORD + key: DATABASE_PASSWORD - name: NETWORK value: Celo - name: SUBNETWORK @@ -43,7 +43,17 @@ volumes: - name: ETHEREUM_JSONRPC_WS_URL value: {{ .Values.blockscout.jsonrpc_ws_url }} - name: DATABASE_URL - value: postgres://$(DB_USERNAME):$(DB_PASSWORD)@127.0.0.1:5432/{{ .Values.blockscout.db.name }} + value: postgres://$(DATABASE_USER):$(DATABASE_PASSWORD)@127.0.0.1:5432/{{ .Values.blockscout.db.name }} +- name: DATABASE_DB + value: {{ .Values.blockscout.db.name }} +- name: DATABASE_HOSTNAME + value: "127.0.0.1" +- name: DATABASE_PORT + value: "5432" +- name: MIX_ENV + value: prod +- name: LOGO + value: /images/celo_logo.svg {{- end -}} {{- define "celo.prom-to-sd-container" -}} diff --git a/packages/helm-charts/blockscout/templates/blockscout-indexer.deployment.yaml b/packages/helm-charts/blockscout/templates/blockscout-indexer.deployment.yaml index 6d84aa59ec7..296ca77ac08 100644 --- a/packages/helm-charts/blockscout/templates/blockscout-indexer.deployment.yaml +++ b/packages/helm-charts/blockscout/templates/blockscout-indexer.deployment.yaml @@ -24,8 +24,14 @@ spec: spec: containers: - name: blockscout-indexer - image: {{ .Values.blockscout.image.repository }}:{{ .Values.blockscout.image.indexerTag }} + image: {{ .Values.blockscout.image.repository }}:{{ .Values.blockscout.image.tag }} imagePullPolicy: {{ .Values.imagePullPolicy }} + command: + - /bin/sh + - -c + args: + - | + mix cmd --app indexer iex -S mix ports: - name: http containerPort: 4000 diff --git a/packages/helm-charts/blockscout/templates/blockscout-migration.job.yaml b/packages/helm-charts/blockscout/templates/blockscout-migration.job.yaml index 2169e49afc5..7dd939c20af 100644 --- a/packages/helm-charts/blockscout/templates/blockscout-migration.job.yaml +++ b/packages/helm-charts/blockscout/templates/blockscout-migration.job.yaml @@ -13,7 +13,7 @@ spec: spec: containers: - name: blockscout-web - image: {{ .Values.blockscout.image.repository }}:{{ .Values.blockscout.image.webTag }} + image: {{ .Values.blockscout.image.repository }}:{{ .Values.blockscout.image.tag }} imagePullPolicy: {{ .Values.imagePullPolicy }} command: ["/bin/sh"] args: ["-c", "echo Sleeping for 15; sleep 15; mix ecto.migrate"] diff --git a/packages/helm-charts/blockscout/templates/blockscout-web.deployment.yaml b/packages/helm-charts/blockscout/templates/blockscout-web.deployment.yaml index 985db64e0cd..e23b7c8e883 100644 --- a/packages/helm-charts/blockscout/templates/blockscout-web.deployment.yaml +++ b/packages/helm-charts/blockscout/templates/blockscout-web.deployment.yaml @@ -24,8 +24,14 @@ spec: spec: containers: - name: blockscout-web - image: {{ .Values.blockscout.image.repository }}:{{ .Values.blockscout.image.webTag }} + image: {{ .Values.blockscout.image.repository }}:{{ .Values.blockscout.image.tag }} imagePullPolicy: {{ .Values.imagePullPolicy }} + command: + - /bin/sh + - -c + args: + - | + mix cmd --app block_scout_web mix phx.server ports: - name: http containerPort: 4000 diff --git a/packages/helm-charts/blockscout/templates/blockscout.secret.yaml b/packages/helm-charts/blockscout/templates/blockscout.secret.yaml index 9b86c90e8b2..a08eb2a4e71 100644 --- a/packages/helm-charts/blockscout/templates/blockscout.secret.yaml +++ b/packages/helm-charts/blockscout/templates/blockscout.secret.yaml @@ -9,5 +9,5 @@ metadata: heritage: {{ .Release.Service }} type: Opaque data: - DB_USERNAME: {{ .Values.blockscout.db.username | b64enc | quote }} - DB_PASSWORD: {{ .Values.blockscout.db.password | b64enc | quote }} + DATABASE_USER: {{ .Values.blockscout.db.username | b64enc | quote }} + DATABASE_PASSWORD: {{ .Values.blockscout.db.password | b64enc | quote }} diff --git a/packages/helm-charts/blockscout/values.yaml b/packages/helm-charts/blockscout/values.yaml index 4b098d76570..98b11d8240d 100644 --- a/packages/helm-charts/blockscout/values.yaml +++ b/packages/helm-charts/blockscout/values.yaml @@ -7,8 +7,7 @@ promtosd: blockscout: image: repository: gcr.io/celo-testnet/blockscout - webTag: web - indexerTag: indexer + tag: v2.0.4-beta-celo db: # ip: must be provided at runtime # IP address of the postgres DB # connection_name: must be provided at runtime # name of the cloud sql connection diff --git a/packages/helm-charts/ethstats/values.yaml b/packages/helm-charts/ethstats/values.yaml index cc719bbcd7c..0228995fc9f 100644 --- a/packages/helm-charts/ethstats/values.yaml +++ b/packages/helm-charts/ethstats/values.yaml @@ -6,7 +6,7 @@ nodeSelector: {} ethstats: image: - repository: ethereumex/eth-stats-dashboard - tag: v0.0.1 + repository: gcr.io/celo-testnet/ethstats + tag: latest service: type: NodePort diff --git a/packages/helm-charts/testnet/templates/_helpers.tpl b/packages/helm-charts/testnet/templates/_helpers.tpl index c70d3420442..9fa6663aebe 100644 --- a/packages/helm-charts/testnet/templates/_helpers.tpl +++ b/packages/helm-charts/testnet/templates/_helpers.tpl @@ -192,28 +192,28 @@ spec: args: - "-c" - "geth --bootnodes=`cat /root/.celo/bootnodes` \ - --password=/root/.celo/account/accountSecret \ - --nodekey=/root/.celo/account/{{ .Node.name}}PrivateKey \ - --unlock=${ACCOUNT_ADDRESS} \ + --consoleformat=json \ + --consoleoutput=stdout \ + --etherbase=${ACCOUNT_ADDRESS} \ + --ethstats=${HOSTNAME}:${ETHSTATS_SECRET}@${ETHSTATS_SVC} \ + --metrics \ --mine \ + --miner.verificationpool=${VERIFICATION_POOL_URL} \ + --networkid=${NETWORK_ID} \ + --nodekey=/root/.celo/account/{{ .Node.name}}PrivateKey \ + --password=/root/.celo/account/accountSecret \ --rpc \ --rpcaddr 0.0.0.0 \ - --rpcapi=eth,net,web3,debug \ - --rpccorsdomain='*' \ + --rpcapi=eth,net,web3,debug,txpool \ + --rpccorsdomain=* \ --rpcvhosts=* \ - --ws \ - --wsaddr 0.0.0.0 \ - --wsorigins=* \ - --wsapi=eth,net,web3,debug \ - --etherbase=${ACCOUNT_ADDRESS} \ - --networkid=${NETWORK_ID} \ - --miner.verificationpool=${VERIFICATION_POOL_URL} \ --syncmode=full \ - --ethstats=${HOSTNAME}:${ETHSTATS_SECRET}@${ETHSTATS_SVC} \ - --consoleformat=json \ - --consoleoutput=stdout \ + --unlock=${ACCOUNT_ADDRESS} \ --verbosity={{ .Values.geth.verbosity }} \ - --metrics" + --ws \ + --wsaddr 0.0.0.0 \ + --wsapi=eth,net,web3,debug,txpool \ + --wsorigins=*" ports: - name: discovery-udp containerPort: 30303 @@ -310,25 +310,25 @@ spec: args: - "-c" - "geth --bootnodes=`cat /root/.celo/bootnodes` \ - --lightserv 90 \ - --lightpeers 250 \ - --networkid=${NETWORK_ID} \ - --ethstats=${HOSTNAME}:${ETHSTATS_SECRET}@${ETHSTATS_SVC} \ --consoleformat=json \ --consoleoutput=stdout \ - --verbosity={{ .Values.geth.verbosity }} \ + --ethstats=${HOSTNAME}:${ETHSTATS_SECRET}@${ETHSTATS_SVC} \ + --lightpeers 250 \ + --lightserv 90 \ --metrics \ - --targetgaslimit=${TARGET_GAS_LIMIT} \ + --networkid=${NETWORK_ID} \ + --nodekey=/root/.celo/account/{{ .node_name }}NodeKey \ --rpc \ --rpcaddr 0.0.0.0 \ - --rpcapi=eth,net,web3,debug \ - --rpccorsdomain='*' \ + --rpcapi=eth,net,web3,debug,txpool \ + --rpccorsdomain=* \ --rpcvhosts=* \ + --targetgaslimit=${TARGET_GAS_LIMIT} \ + --verbosity={{ .Values.geth.verbosity }} \ --ws \ --wsaddr 0.0.0.0 \ - --wsorigins=* \ - --wsapi=eth,net,web3,debug \ - --nodekey=/root/.celo/account/{{ .node_name }}NodeKey" + --wsapi=eth,net,web3,debug,txpool \ + --wsorigins=*" ports: - name: discovery-udp containerPort: 30303 diff --git a/packages/helm-charts/testnet/templates/infura.ingress.yaml b/packages/helm-charts/testnet/templates/forno.ingress.yaml similarity index 51% rename from packages/helm-charts/testnet/templates/infura.ingress.yaml rename to packages/helm-charts/testnet/templates/forno.ingress.yaml index e2ce3d95095..9833e2c5da1 100644 --- a/packages/helm-charts/testnet/templates/infura.ingress.yaml +++ b/packages/helm-charts/testnet/templates/forno.ingress.yaml @@ -1,22 +1,22 @@ apiVersion: extensions/v1beta1 kind: Ingress metadata: - name: {{ .Release.Namespace }}-infura-ingress + name: {{ .Release.Namespace }}-forno-ingress labels: - app: infura + app: forno chart: testnet release: {{ .Release.Namespace }} - heritage: infura - component: infura-web-ingress + heritage: forno + component: forno-web-ingress annotations: kubernetes.io/tls-acme: "true" spec: tls: - hosts: - - {{ .Release.Namespace }}-infura.{{ .Values.domain.name }}.org - secretName: {{ .Release.Namespace }}-web-tls + - {{ .Release.Namespace }}-forno.{{ .Values.domain.name }}.org + secretName: {{ .Release.Namespace }}-forno-web-tls rules: - - host: {{ .Release.Namespace }}-infura.{{ .Values.domain.name }}.org + - host: {{ .Release.Namespace }}-forno.{{ .Values.domain.name }}.org http: paths: - path: / diff --git a/packages/mobile/.env b/packages/mobile/.env index 3b0e51934b2..c1bce8d0cd8 100644 --- a/packages/mobile/.env +++ b/packages/mobile/.env @@ -1,8 +1,10 @@ ENVIRONMENT=local -DEFAULT_TESTNET=integration -# -1 == ZeroSync, 5 == Ultralight, see src/geth/consts.ts for more info +DEFAULT_TESTNET=alfajores +# If ZERO_SYNC_ENABLED_INITIALLY, local geth will not run initially. +# If toggled on, it will use DEFAULT_SYNC_MODE. See src/geth/consts.ts for more info +ZERO_SYNC_ENABLED_INITIALLY=false DEFAULT_SYNC_MODE=5 DEV_SETTINGS_ACTIVE_INITIALLY=true FIREBASE_ENABLED=true SECRETS_KEY=debug -SHOW_TESTNET_BANNER=true +SHOW_TESTNET_BANNER=true \ No newline at end of file diff --git a/packages/mobile/.env.alfajores b/packages/mobile/.env.alfajores index b8c02dc9997..2f6e79d4622 100644 --- a/packages/mobile/.env.alfajores +++ b/packages/mobile/.env.alfajores @@ -1,6 +1,8 @@ ENVIRONMENT=alfajores DEFAULT_TESTNET=alfajores -# -1 == ZeroSync, 5 == Ultralight, see src/geth/consts.ts for more info +# If ZERO_SYNC_ENABLED_INITIALLY, local geth will not run initially. +# If toggled on, it will use DEFAULT_SYNC_MODE. See src/geth/consts.ts for more info +ZERO_SYNC_ENABLED_INITIALLY=false DEFAULT_SYNC_MODE=5 DEV_SETTINGS_ACTIVE_INITIALLY=false FIREBASE_ENABLED=true diff --git a/packages/mobile/.env.integration b/packages/mobile/.env.integration index 95cba3a10d9..381c53f3952 100644 --- a/packages/mobile/.env.integration +++ b/packages/mobile/.env.integration @@ -1,6 +1,8 @@ ENVIRONMENT=integration DEFAULT_TESTNET=integration -# -1 == ZeroSync, 5 == Ultralight, see src/geth/consts.ts for more info +# If ZERO_SYNC_ENABLED_INITIALLY, local geth will not run initially. +# If toggled on, it will use DEFAULT_SYNC_MODE. See src/geth/consts.ts for more info +ZERO_SYNC_ENABLED_INITIALLY=false DEFAULT_SYNC_MODE=5 DEV_SETTINGS_ACTIVE_INITIALLY=true FIREBASE_ENABLED=true diff --git a/packages/mobile/.env.pilot b/packages/mobile/.env.pilot index 8f8783f5dc8..ae5455d865f 100644 --- a/packages/mobile/.env.pilot +++ b/packages/mobile/.env.pilot @@ -1,6 +1,8 @@ ENVIRONMENT=pilot DEFAULT_TESTNET=pilot -# -1 == ZeroSync, 5 == Ultralight, see src/geth/consts.ts for more info +# If ZERO_SYNC_ENABLED_INITIALLY, local geth will not run initially. +# If toggled on, it will use DEFAULT_SYNC_MODE. See src/geth/consts.ts for more info +ZERO_SYNC_ENABLED_INITIALLY=false DEFAULT_SYNC_MODE=5 DEV_SETTINGS_ACTIVE_INITIALLY=false FIREBASE_ENABLED=true diff --git a/packages/mobile/.env.pilotstaging b/packages/mobile/.env.pilotstaging index f43a2095ea5..c60447aa725 100644 --- a/packages/mobile/.env.pilotstaging +++ b/packages/mobile/.env.pilotstaging @@ -1,6 +1,8 @@ ENVIRONMENT=pilotstaging DEFAULT_TESTNET=pilotstaging -# -1 == ZeroSync, 5 == Ultralight, see src/geth/consts.ts for more info +# If ZERO_SYNC_ENABLED_INITIALLY, local geth will not run initially. +# If toggled on, it will use DEFAULT_SYNC_MODE. See src/geth/consts.ts for more info +ZERO_SYNC_ENABLED_INITIALLY=false DEFAULT_SYNC_MODE=5 DEV_SETTINGS_ACTIVE_INITIALLY=true FIREBASE_ENABLED=true diff --git a/packages/mobile/.env.production b/packages/mobile/.env.production index 6f1313858b3..812aec9945e 100644 --- a/packages/mobile/.env.production +++ b/packages/mobile/.env.production @@ -1,6 +1,8 @@ ENVIRONMENT=production DEFAULT_TESTNET=argentinaproduction -# -1 == ZeroSync, 5 == Ultralight, see src/geth/consts.ts for more info +# If ZERO_SYNC_ENABLED_INITIALLY, local geth will not run initially. +# If toggled on, it will use DEFAULT_SYNC_MODE. See src/geth/consts.ts for more info +ZERO_SYNC_ENABLED_INITIALLY=false DEFAULT_SYNC_MODE=5 DEV_SETTINGS_ACTIVE_INITIALLY=false FIREBASE_ENABLED=true diff --git a/packages/mobile/.env.staging b/packages/mobile/.env.staging index e180a477050..6fff7365b86 100644 --- a/packages/mobile/.env.staging +++ b/packages/mobile/.env.staging @@ -1,6 +1,8 @@ ENVIRONMENT=staging DEFAULT_TESTNET=alfajoresstaging -# -1 == ZeroSync, 5 == Ultralight, see src/geth/consts.ts for more info +# If ZERO_SYNC_ENABLED_INITIALLY, local geth will not run initially. +# If toggled on, it will use DEFAULT_SYNC_MODE. See src/geth/consts.ts for more info +ZERO_SYNC_ENABLED_INITIALLY=false DEFAULT_SYNC_MODE=5 DEV_SETTINGS_ACTIVE_INITIALLY=true FIREBASE_ENABLED=true diff --git a/packages/mobile/.env.test b/packages/mobile/.env.test index ed12cba5665..83a8aac2028 100644 --- a/packages/mobile/.env.test +++ b/packages/mobile/.env.test @@ -1,6 +1,8 @@ ENVIRONMENT=local DEFAULT_TESTNET=integration -# -1 == ZeroSync, 5 == Ultralight, see src/geth/consts.ts for more info +# If ZERO_SYNC_ENABLED_INITIALLY, local geth will not run initially. +# If toggled on, it will use DEFAULT_SYNC_MODE. See src/geth/consts.ts for more info +ZERO_SYNC_ENABLED_INITIALLY=false DEFAULT_SYNC_MODE=5 DEV_SETTINGS_ACTIVE_INITIALLY=true FIREBASE_ENABLED=false diff --git a/packages/mobile/.flowconfig b/packages/mobile/.flowconfig index 47d80d95414..9edb1be387b 100644 --- a/packages/mobile/.flowconfig +++ b/packages/mobile/.flowconfig @@ -5,19 +5,18 @@ ; Ignore "BUCK" generated dirs /\.buckd/ -; Ignore unexpected extra "@providesModule" -.*/node_modules/.*/node_modules/fbjs/.* +; Ignore polyfills +node_modules/react-native/Libraries/polyfills/.* -; Ignore duplicate module providers -; For RN Apps installed via npm, "Libraries" folder is inside -; "node_modules/react-native" but in the source repo it is in the root -.*/Libraries/react-native/React.js +; These should not be required directly +; require from fbjs/lib instead: require('fbjs/lib/warning') +node_modules/warning/.* -; Ignore polyfills -.*/Libraries/polyfills/.* +; Flow doesn't support platforms +.*/Libraries/Utilities/LoadingView.js -; Ignore metro -.*/node_modules/metro/.* +[untyped] +.*/node_modules/@react-native-community/cli/.*/.* [include] @@ -31,39 +30,46 @@ emoji=true esproposal.optional_chaining=enable esproposal.nullish_coalescing=enable -module.system=haste -module.system.haste.use_name_reducers=true -# get basename -module.system.haste.name_reducers='^.*/\([a-zA-Z0-9$_.-]+\.js\(\.flow\)?\)$' -> '\1' -# strip .js or .js.flow suffix -module.system.haste.name_reducers='^\(.*\)\.js\(\.flow\)?$' -> '\1' -# strip .ios suffix -module.system.haste.name_reducers='^\(.*\)\.ios$' -> '\1' -module.system.haste.name_reducers='^\(.*\)\.android$' -> '\1' -module.system.haste.name_reducers='^\(.*\)\.native$' -> '\1' -module.system.haste.paths.blacklist=.*/__tests__/.* -module.system.haste.paths.blacklist=.*/__mocks__/.* -module.system.haste.paths.blacklist=/node_modules/react-native/Libraries/Animated/src/polyfills/.* -module.system.haste.paths.whitelist=/node_modules/react-native/Libraries/.* +module.file_ext=.js +module.file_ext=.json +module.file_ext=.ios.js munge_underscores=true -module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> 'RelativeImageStub' - -module.file_ext=.js -module.file_ext=.jsx -module.file_ext=.json -module.file_ext=.native.js +module.name_mapper='^react-native$' -> '/node_modules/react-native/Libraries/react-native/react-native-implementation' +module.name_mapper='^react-native/\(.*\)$' -> '/node_modules/react-native/\1' +module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> '/node_modules/react-native/Libraries/Image/RelativeImageStub' suppress_type=$FlowIssue suppress_type=$FlowFixMe suppress_type=$FlowFixMeProps suppress_type=$FlowFixMeState -suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\) -suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+ -suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy +suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(\\)? *\\(site=[a-z,_]*react_native\\(_ios\\)?_\\(oss\\|fb\\)[a-z,_]*\\)?)\\) +suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(\\)? *\\(site=[a-z,_]*react_native\\(_ios\\)?_\\(oss\\|fb\\)[a-z,_]*\\)?)\\)?:? #[0-9]+ suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError +[lints] +sketchy-null-number=warn +sketchy-null-mixed=warn +sketchy-number=warn +untyped-type-import=warn +nonstrict-import=warn +deprecated-type=warn +unsafe-getters-setters=warn +inexact-spread=warn +unnecessary-invariant=warn +signature-verification-failure=warn +deprecated-utility=error + +[strict] +deprecated-type +nonstrict-import +sketchy-null +unclear-type +unsafe-getters-setters +untyped-import +untyped-type-import + [version] -^0.92.0 +^0.105.0 \ No newline at end of file diff --git a/packages/mobile/.gitignore b/packages/mobile/.gitignore index 78474411445..57bc3c8624e 100644 --- a/packages/mobile/.gitignore +++ b/packages/mobile/.gitignore @@ -20,7 +20,6 @@ DerivedData *.hmap *.ipa *.xcuserstate -project.xcworkspace # Android/IntelliJ # @@ -40,6 +39,7 @@ yarn-error.log buck-out/ \.buckd/ *.keystore +!debug.keystore # fastlane # @@ -75,14 +75,4 @@ e2e_run.log e2e_pidcat_run.log # keys -android/app/google-services.json -android/app/src/staging/google-services.json -android/app/src/integration/google-services.json -android/app/src/alfajores/google-services.json -android/app/src/debug/google-services.json -android/app/src/pilot/google-services.json secrets.json -android/sentry.properties -ios/**/GoogleService-Info*.plist -ios/sentry.properties -ios/Pods diff --git a/packages/mobile/Gemfile.lock b/packages/mobile/Gemfile.lock index 86f966849b1..a80bf09f932 100644 --- a/packages/mobile/Gemfile.lock +++ b/packages/mobile/Gemfile.lock @@ -163,7 +163,7 @@ GEM retriable (3.1.2) rouge (2.0.7) ruby-macho (1.4.0) - rubyzip (1.2.3) + rubyzip (1.3.0) security (0.1.3) signet (0.11.0) addressable (~> 2.3) diff --git a/packages/mobile/README.md b/packages/mobile/README.md index 01eaff317a4..621cdd665f8 100644 --- a/packages/mobile/README.md +++ b/packages/mobile/README.md @@ -25,7 +25,7 @@ This makes Gradle faster: export GRADLE_OPTS='-Dorg.gradle.daemon=true -Dorg.gradle.parallel=true -Dorg.gradle.jvmargs="-Xmx4096m -XX:+HeapDumpOnOutOfMemoryError"' ``` -## Running +## Running the App 1. If you haven't already, run `yarn` from the monorepo root to install dependencies. @@ -97,6 +97,12 @@ yarn run build-sdk TESTNET before rebuilding the app. Note that this will assume the testnets have a corresponding `/blockchain-api` and `/notification-service` set up. +### Running Wallet app in ZeroSync mode + +By default, the mobile wallet app runs geth in ultralight sync mode where all the epoch headers are fetched. The default sync mode is defined in [packages/mobile/.env](https://github.com/celo-org/celo-monorepo/blob/master/packages/mobile/.env#L4) file. + +To run wallet in zero sync mode, where it would connect to the remote nodes and sign transactions in web3, change the default sync mode in the aforementioned file to -1. The mode has only been tested on Android and is hard-coded to be [crash](https://github.com/celo-org/celo-monorepo/blob/aeddeefbfb230db51d2ef76d50c5f882644a1cd3/packages/mobile/src/web3/contracts.ts#L73) on iOS. + ## Testing To execute the suite of tests, run `yarn test` @@ -160,6 +166,11 @@ export CELO_RELEASE_KEY_PASSWORD=celoFakeReleaseKeyPass ### Building an APK or Bundle ```sh +# With fastlane: +bundle install +bundle exec fastlane android build_apk env:YOUR_BUILDING_VARIANT sdkEnv:YOUR_SDK_ENV + +# Or, manually cd android/ ./gradlew clean ./gradlew bundle{YOUR_BUILDING_VARIANT}JsAndAssets @@ -171,25 +182,25 @@ cd android/ Where `YOUR_BUILD_VARIANT` can be any of the app's build variants, such as debug or release. +## Configuring the SMS Retriever + +On android, the wallet app uses the SMS Retriever API to automatically input codes during phone number verification. + +The service that route SMS messages to the app needs to be configured to [append this app signature to the message][sms retriever]. Note, the signature will need to be computed using the signing key from the google play dashboard. + ## Generating GraphQL Types We're using [GraphQL Code Generator][graphql code generator] to properly type GraphQL queries. If you make a change to a query, run `yarn build:gen-graphql-types` to update the typings in the `typings` directory. -## Running Wallet app in ZeroSync mode - -By default, the mobile wallet app runs geth in ultralight sync mode where all the epoch headers are fetched. The default sync mode is defined in [packages/mobile/.env](https://github.com/celo-org/celo-monorepo/blob/master/packages/mobile/.env#L4) file. - -To run wallet in zero sync mode, where it would connect to the remote nodes and sign transactions in web3, change the default sync mode in the aforementioned file to -1. The mode has only been tested on Android and is hard-coded to be [crash](https://github.com/celo-org/celo-monorepo/blob/aeddeefbfb230db51d2ef76d50c5f882644a1cd3/packages/mobile/src/web3/contracts.ts#L73) on iOS. -======= ## How we handle Geth crashes in wallet app on Android Our Celo app has three types of codes. -1. Javascript code - generated from Typescript, this runs in Javascript interpreter. -2. Java bytecode - This runs on Dalvik/Art Virtual Machine. -3. Native code - Geth code is written in Golang which compiles to native code - this runs directly on the -CPU, no virtual machines involved. +1. Javascript code - generated from Typescript, this runs in Javascript interpreter. +2. Java bytecode - This runs on Dalvik/Art Virtual Machine. +3. Native code - Geth code is written in Golang which compiles to native code - this runs directly on the + CPU, no virtual machines involved. One should note that, on iOS, there is no byte code and therefore, there are only two layers, one is the Javascript code, and the other is the Native code. Till now, we have been blind towards native crashes except Google Playstore logs. @@ -199,14 +210,14 @@ We cannot use libraries like [Bugsnag](https://www.bugsnag.com) since they do no Relevant code references: -1. [NDKCrashService](https://github.com/celo-org/celo-monorepo/blob/master/packages/mobile/android/app/src/main/java/org/celo/mobile/NdkCrashService.java) -2. [Initialization](https://github.com/celo-org/celo-monorepo/blob/8689634a1d10d74ba6d4f3b36b2484db60a95bdb/packages/mobile/android/app/src/main/java/org/celo/mobile/MainApplication.java#L156) of the NDKCrashService -3. [Sentry code](https://github.com/celo-org/celo-monorepo/blob/799d74675dc09327543c210e88cbf5cc796721a0/packages/mobile/src/sentry/Sentry.ts#L53) to read NDK crash logs on restart +1. [NDKCrashService](https://github.com/celo-org/celo-monorepo/blob/master/packages/mobile/android/app/src/main/java/org/celo/mobile/NdkCrashService.java) +2. [Initialization](https://github.com/celo-org/celo-monorepo/blob/8689634a1d10d74ba6d4f3b36b2484db60a95bdb/packages/mobile/android/app/src/main/java/org/celo/mobile/MainApplication.java#L156) of the NDKCrashService +3. [Sentry code](https://github.com/celo-org/celo-monorepo/blob/799d74675dc09327543c210e88cbf5cc796721a0/packages/mobile/src/sentry/Sentry.ts#L53) to read NDK crash logs on restart There are two major differencs in ZeroSync mode: -1. Geth won't run at all. The web3 would instead connect to -infura.celo-testnet.org using an https provider, for example, [https://integration-infura.celo-testnet.org](https://integration-infura.celo-testnet.org). -2. Changes to [sendTransactionAsyncWithWeb3Signing](https://github.com/celo-org/celo-monorepo/blob/8689634a1d10d74ba6d4f3b36b2484db60a95bdb/packages/walletkit/src/contract-utils.ts#L362) in walletkit to poll after sending a transaction for the transaction to succeed. This is needed because http provider, unliked web sockets and IPC provider, does not support subscriptions. +1. Geth won't run at all. The web3 would instead connect to -forno.celo-testnet.org using an https provider, for example, [https://integration-forno.celo-testnet.org](https://integration-forno.celo-testnet.org). +2. Changes to [sendTransactionAsyncWithWeb3Signing](https://github.com/celo-org/celo-monorepo/blob/8689634a1d10d74ba6d4f3b36b2484db60a95bdb/packages/walletkit/src/contract-utils.ts#L362) in walletkit to poll after sending a transaction for the transaction to succeed. This is needed because http provider, unliked web sockets and IPC provider, does not support subscriptions. #### Why http(s) provider? @@ -254,3 +265,4 @@ $ adb kill-server && adb start-server [rntl-docs]: https://callstack.github.io/react-native-testing-library/ [jest]: https://jestjs.io/docs/en/snapshot-testing [redux-saga-test-plan]: https://github.com/jfairbank/redux-saga-test-plan +[sms retriever]: https://developers.google.com/identity/sms-retriever/verify#1_construct_a_verification_message diff --git a/packages/mobile/__mocks__/@celo/react-native-sms-retriever.ts b/packages/mobile/__mocks__/@celo/react-native-sms-retriever.ts index 04b9d46e495..7b951669c87 100644 --- a/packages/mobile/__mocks__/@celo/react-native-sms-retriever.ts +++ b/packages/mobile/__mocks__/@celo/react-native-sms-retriever.ts @@ -1,7 +1,6 @@ -import { NativeModules } from 'react-native' - -NativeModules.RNSmsRetrieverModule = {} - -const smsRetrieverMock = jest.genMockFromModule('@celo/react-native-sms-retriever') - -module.exports = smsRetrieverMock +export default { + requestPhoneNumber: jest.fn(), + startSmsRetriever: jest.fn(), + addSmsListener: jest.fn(), + removeSmsListener: jest.fn(), +} diff --git a/packages/mobile/__mocks__/@react-native-community/async-storage.js b/packages/mobile/__mocks__/@react-native-community/async-storage.js new file mode 100644 index 00000000000..7ea8216f047 --- /dev/null +++ b/packages/mobile/__mocks__/@react-native-community/async-storage.js @@ -0,0 +1,3 @@ +import mock from '@react-native-community/async-storage/jest/async-storage-mock' + +export default mock diff --git a/packages/mobile/__mocks__/@sentry/react-native.ts b/packages/mobile/__mocks__/@sentry/react-native.ts new file mode 100644 index 00000000000..1283e626917 --- /dev/null +++ b/packages/mobile/__mocks__/@sentry/react-native.ts @@ -0,0 +1,7 @@ +const Sentry = { + config: () => ({ install: jest.fn() }), + setTagsContext: jest.fn(), + captureException: jest.fn(), +} + +module.exports = Sentry diff --git a/packages/mobile/__mocks__/react-i18next.jsx b/packages/mobile/__mocks__/react-i18next.tsx similarity index 100% rename from packages/mobile/__mocks__/react-i18next.jsx rename to packages/mobile/__mocks__/react-i18next.tsx diff --git a/packages/mobile/__mocks__/react-native-device-info.ts b/packages/mobile/__mocks__/react-native-device-info.ts index cb182c5cafc..6d6713cf196 100644 --- a/packages/mobile/__mocks__/react-native-device-info.ts +++ b/packages/mobile/__mocks__/react-native-device-info.ts @@ -1,5 +1,6 @@ export default { - getAPILevel: jest.fn(), + getApiLevel: jest.fn(), + getApiLevelSync: jest.fn(), getApplicationName: jest.fn(), getBrand: jest.fn(), getBuildNumber: jest.fn(), diff --git a/packages/mobile/__mocks__/react-native-randombytes.ts b/packages/mobile/__mocks__/react-native-randombytes.ts index c1c4cf2794e..a31853f76e2 100644 --- a/packages/mobile/__mocks__/react-native-randombytes.ts +++ b/packages/mobile/__mocks__/react-native-randombytes.ts @@ -1,5 +1,5 @@ export const randomBytes = jest.fn(() => ({ - toString: jest.fn(), + toString: jest.fn(() => '123'), })) export default {} diff --git a/packages/mobile/__mocks__/react-native-sentry.ts b/packages/mobile/__mocks__/react-native-sentry.ts deleted file mode 100644 index 8c98f4c138d..00000000000 --- a/packages/mobile/__mocks__/react-native-sentry.ts +++ /dev/null @@ -1,7 +0,0 @@ -const Sentry = { - config: () => ({ install: () => {} }), - setTagsContext: () => {}, - captureException: () => {}, -} - -export { Sentry } diff --git a/packages/mobile/__mocks__/src/navigator/NavigationService.ts b/packages/mobile/__mocks__/src/navigator/NavigationService.ts index 75b94c6609b..263e01e5b87 100644 --- a/packages/mobile/__mocks__/src/navigator/NavigationService.ts +++ b/packages/mobile/__mocks__/src/navigator/NavigationService.ts @@ -1 +1,6 @@ -export function navigate() {} +export const navigate = jest.fn() +export const navigateBack = jest.fn() + +export enum NavActions { + SET_NAVIGATOR = 'NAVIGATION/SET_NAVIGATOR', +} diff --git a/packages/mobile/__mocks__/src/pincode/PincodeCache.ts b/packages/mobile/__mocks__/src/pincode/PincodeCache.ts new file mode 100644 index 00000000000..89fc8bf81bb --- /dev/null +++ b/packages/mobile/__mocks__/src/pincode/PincodeCache.ts @@ -0,0 +1,2 @@ +export const getCachedPincode = jest.fn(() => '123456') +export const setCachedPincode = jest.fn() diff --git a/packages/mobile/__mocks__/src/pincode/PincodeUtils.ts b/packages/mobile/__mocks__/src/pincode/PincodeUtils.ts new file mode 100644 index 00000000000..70b39c8407e --- /dev/null +++ b/packages/mobile/__mocks__/src/pincode/PincodeUtils.ts @@ -0,0 +1,2 @@ +export const getPinFromKeystore = jest.fn(() => '123456') +export const setPinInKeystore = jest.fn() diff --git a/packages/mobile/android/.gitignore b/packages/mobile/android/.gitignore new file mode 100644 index 00000000000..afbc536e065 --- /dev/null +++ b/packages/mobile/android/.gitignore @@ -0,0 +1,10 @@ +# keys +app/google-services.json +app/src/staging/google-services.json +app/src/integration/google-services.json +app/src/alfajores/google-services.json +app/src/debug/google-services.json +app/src/pilot/google-services.json +sentry.properties + +*.hprof \ No newline at end of file diff --git a/packages/mobile/android/app/build.gradle b/packages/mobile/android/app/build.gradle index 4b98974470a..2c731a5dfd3 100644 --- a/packages/mobile/android/app/build.gradle +++ b/packages/mobile/android/app/build.gradle @@ -74,6 +74,7 @@ import com.android.build.OutputFile project.ext.react = [ entryFile : "index.js", + enableHermes: true, // clean and rebuild if changing extraPackagerArgs: ["--max-workers=2"], bundleInIntegration: true, devDisabledInIntegration: true, @@ -83,6 +84,9 @@ project.ext.react = [ devDisabledInAlfajores: true, bundleInPilot: true, devDisabledInPilot: true, + cliPath: "../../node_modules/react-native/cli.js", + composeSourceMapsPath: "../../node_modules/react-native/scripts/compose-source-maps.js", + hermesCommand: "../../../../node_modules/hermes-engine/%OS-BIN%/hermes" ] project.ext.envConfigFiles = [ @@ -95,8 +99,8 @@ project.ext.envConfigFiles = [ ] apply from: project(':react-native-config').projectDir.getPath() + "/dotenv.gradle" -apply from: "../../node_modules/react-native/react.gradle" -apply from: "../../../../node_modules/react-native-sentry/sentry.gradle" +apply from: "./react.gradle" +apply from: "../../../../node_modules/@sentry/react-native/sentry.gradle" /** @@ -110,6 +114,28 @@ def isDetoxTestBuild = !gradle.startParameter.taskNames.contains("assembleAndroi */ def enableProguardInReleaseBuilds = true +/** + * The preferred build flavor of JavaScriptCore. + * + * For example, to use the international variant, you can use: + * `def jscFlavor = 'org.webkit:android-jsc-intl:+'` + * + * The international variant includes ICU i18n library and necessary data + * allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that + * give correct results when using with locales other than en-US. Note that + * this variant is about 6MiB larger per architecture than default. + */ +def jscFlavor = 'org.webkit:android-jsc:+' + +/** + * Whether to enable the Hermes VM. + * + * This should be set on project.ext.react and mirrored here. If it is not set + * on project.ext.react, JavaScript will not be compiled to Hermes Bytecode + * and the benefits of using Hermes will therefore be sharply reduced. + */ +def enableHermes = project.ext.react.get("enableHermes", false); + def appVersionCode = Integer.valueOf(System.env.CIRCLE_BUILD_NUM ?: VERSION_CODE) android { @@ -125,7 +151,7 @@ android { minSdkVersion isDetoxTestBuild ? rootProject.ext.minSdkVersion : 18 targetSdkVersion rootProject.ext.targetSdkVersion versionCode appVersionCode - versionName "1.5.0" + versionName "1.5.1" testBuildType System.getProperty('testBuildType', 'debug') testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" resValue "string", "build_config_package", "org.celo.mobile" @@ -140,6 +166,13 @@ android { keyPassword System.getenv("CELO_RELEASE_KEY_PASSWORD") } } + + debug { + storeFile file('debug.keystore') + storePassword 'android' + keyAlias 'androiddebugkey' + keyPassword 'android' + } } splits { abi { @@ -161,6 +194,7 @@ android { debug { applicationIdSuffix ".debug" multiDexEnabled true + signingConfig signingConfigs.debug } integration { @@ -205,9 +239,8 @@ android { // applicationVariants are e.g. debug, release applicationVariants.all { variant -> variant.outputs.each { output -> - // For each separate APK per architecture, set a unique version code as described here: - // http://tools.android.com/tech-docs/new-build-system/user-guide/apk-splits - def versionCodes = ["armeabi-v7a":1, "x86":2, "arm64-v8a": 3, "x86_64": 4] + // https://developer.android.com/studio/build/configure-apk-splits.html + def versionCodes = ["armeabi-v7a":1, "x86":2, "arm64-v8a": 4, "x86_64": 5] def abi = output.getFilter(OutputFile.ABI) if (abi != null) { // null for the universal-debug, universal-release variants output.versionCodeOverride = @@ -220,59 +253,44 @@ android { exclude 'META-INF/-no-jdk.kotlin_module' exclude 'META-INF/androidx.exifinterface_exifinterface.version' } + + + dexOptions { + javaMaxHeapSize "3g" + } } dependencies { - implementation project(':react-native-install-referrer') - implementation project(':react-native-send-intent') - // implementation project(':react-native-screens') - implementation project(':react-native-webview') - implementation project(':@react-native-community_netinfo') - implementation project(':react-native-gesture-handler') - implementation project(':@segment_analytics-react-native-firebase') - implementation project(':@segment_analytics-react-native') - implementation project(':react-native-camera') - implementation project(':react-native-mail') - implementation project(':react-native-splash-screen') - implementation project(':react-native-version-check') - implementation project(':react-native-android-open-settings') - implementation project(':react-native-udp') - implementation project(':react-native-localize') - implementation project(':react-native-config') - implementation project(':react-native-tcp') - implementation project(':react-native-sentry') - implementation project(':react-native-secure-randombytes') - implementation project(':react-native-svg') - implementation project(':react-native-contacts') - implementation project(':react-native-keep-awake') - implementation project(':react-native-device-info') - implementation project(':react-native-fs') - implementation project(':react-native-geth') - implementation project(':react-native-flag-secure-android') - implementation project(':react-native-firebase') - implementation project(':react-native-confirm-device-credentials') - implementation project(':react-native-restart-android') - implementation project(':react-native-sms-retriever') - implementation project(':react-native-share') - implementation project(':react-native-secure-key-store') - implementation project(':react-native-safe-area-context') - androidTestImplementation('com.wix:detox:+') { transitive = true } androidTestImplementation 'junit:junit:4.12' - implementation 'com.google.android.gms:play-services-base:16.1.0' - implementation 'com.google.firebase:firebase-core:16.0.8' - implementation 'com.google.firebase:firebase-messaging:17.6.0' - implementation 'com.google.firebase:firebase-database:16.1.0' - implementation 'com.google.firebase:firebase-auth:16.2.1' - implementation 'com.google.firebase:firebase-invites:16.1.1' - implementation 'com.google.firebase:firebase-storage:16.1.0' + implementation platform("com.google.firebase:firebase-bom:22.2.0") + implementation 'com.google.android.gms:play-services-base' + implementation 'com.google.firebase:firebase-core' + implementation 'com.google.firebase:firebase-messaging' + implementation 'com.google.firebase:firebase-database' + implementation 'com.google.firebase:firebase-auth' + implementation 'com.google.firebase:firebase-invites' + implementation 'com.google.firebase:firebase-storage' implementation 'ru.ivanarh.ndcrash:jndcrash-libunwind:0.7' - + implementation 'androidx.appcompat:appcompat:1.1.0-rc01' + implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0-alpha02' + implementation project(':react-native-geth') + implementation project(':react-native-confirm-device-credentials') implementation fileTree(dir: "libs", include: ["*.jar"]) - implementation "com.android.support:appcompat-v7:${rootProject.ext.supportLibVersion}" implementation 'com.facebook.react:react-native:+' // From node_modules - implementation 'com.android.support:multidex:1.0.3' + + if (enableHermes) { + def hermesPath = "../../../../node_modules/hermes-engine/android/"; + debugImplementation files(hermesPath + "hermes-debug.aar") + integrationImplementation files(hermesPath + "hermes-release.aar") + stagingImplementation files(hermesPath + "hermes-release.aar") + alfajoresImplementation files(hermesPath + "hermes-release.aar") + pilotImplementation files(hermesPath + "hermes-release.aar") + releaseImplementation files(hermesPath + "hermes-release.aar") + } else { + implementation jscFlavor + } } // Run this once to be able to run the application with BUCK @@ -291,3 +309,11 @@ if (enableFirebase.toBoolean()){ println 'Info: Firebase is disabled in .env variable' } +// https://github.com/facebook/react-native/issues/26400#issuecomment-539395814 +configurations.all { + resolutionStrategy { + force "com.facebook.soloader:soloader:0.8.0" + } +} + +apply from: file("../../../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project) \ No newline at end of file diff --git a/packages/mobile/android/app/debug.keystore b/packages/mobile/android/app/debug.keystore new file mode 100644 index 00000000000..364e105ed39 Binary files /dev/null and b/packages/mobile/android/app/debug.keystore differ diff --git a/packages/mobile/android/app/proguard-rules.pro b/packages/mobile/android/app/proguard-rules.pro index a28e641d737..456fc255a96 100644 --- a/packages/mobile/android/app/proguard-rules.pro +++ b/packages/mobile/android/app/proguard-rules.pro @@ -9,14 +9,6 @@ # Add any project specific keep options here: -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -# public *; -#} - - # RN Firebase -keep class io.invertase.firebase.** { *; } -dontwarn io.invertase.firebase.** diff --git a/packages/mobile/android/app/react.gradle b/packages/mobile/android/app/react.gradle new file mode 100644 index 00000000000..87b6c201c8a --- /dev/null +++ b/packages/mobile/android/app/react.gradle @@ -0,0 +1,310 @@ +// Copyright (c) Facebook, Inc. and its affiliates. + +// This source code is licensed under the MIT license found in the +// LICENSE file in the root directory of this source tree. + +import org.apache.tools.ant.taskdefs.condition.Os + +def config = project.hasProperty("react") ? project.react : []; + +def cliPath = config.cliPath ?: "node_modules/react-native/cli.js" +def composeSourceMapsPath = config.composeSourceMapsPath ?: "node_modules/react-native/scripts/compose-source-maps.js" +def bundleAssetName = config.bundleAssetName ?: "index.android.bundle" +def entryFile = config.entryFile ?: "index.android.js" +def bundleCommand = config.bundleCommand ?: "bundle" +def reactRoot = file(config.root ?: "../../") +def inputExcludes = config.inputExcludes ?: ["android/**", "ios/**"] +def bundleConfig = config.bundleConfig ? "${reactRoot}/${config.bundleConfig}" : null ; +def enableVmCleanup = config.enableVmCleanup == null ? true : config.enableVmCleanup +def hermesCommand = config.hermesCommand ?: "../../node_modules/hermes-engine/%OS-BIN%/hermes" + +def reactNativeDevServerPort() { + def value = project.getProperties().get("reactNativeDevServerPort") + return value != null ? value : "8081" +} + +def reactNativeInspectorProxyPort() { + def value = project.getProperties().get("reactNativeInspectorProxyPort") + return value != null ? value : reactNativeDevServerPort() +} + +def getHermesOSBin() { + if (Os.isFamily(Os.FAMILY_WINDOWS)) return "win64-bin"; + if (Os.isFamily(Os.FAMILY_MAC)) return "osx-bin"; + if (Os.isOs(null, "linux", "amd64", null)) return "linux64-bin"; + throw new Exception("OS not recognized. Please set project.ext.react.hermesCommand " + + "to the path of a working Hermes compiler."); +} + +// Make sure not to inspect the Hermes config unless we need it, +// to avoid breaking any JSC-only setups. +def getHermesCommand = { + // If the project specifies a Hermes command, don't second guess it. + if (!hermesCommand.contains("%OS-BIN%")) { + return hermesCommand + } + + // Execution on Windows fails with / as separator + return hermesCommand + .replaceAll("%OS-BIN%", getHermesOSBin()) + .replace('/' as char, File.separatorChar); +} + +// Set enableHermesForVariant to a function to configure per variant, +// or set `enableHermes` to True/False to set all of them +def enableHermesForVariant = config.enableHermesForVariant ?: { + def variant -> config.enableHermes ?: false +} + +android { + buildTypes.all { + resValue "integer", "react_native_dev_server_port", reactNativeDevServerPort() + resValue "integer", "react_native_inspector_proxy_port", reactNativeInspectorProxyPort() + } +} + +afterEvaluate { + def isAndroidLibrary = plugins.hasPlugin("com.android.library") + def variants = isAndroidLibrary ? android.libraryVariants : android.applicationVariants + variants.all { def variant -> + // Create variant and target names + def targetName = variant.name.capitalize() + def targetPath = variant.dirName + + // React js bundle directories + def jsBundleDir = file("$buildDir/generated/assets/react/${targetPath}") + def resourcesDir = file("$buildDir/generated/res/react/${targetPath}") + + def jsBundleFile = file("$jsBundleDir/$bundleAssetName") + def jsSourceMapsDir = file("$buildDir/generated/sourcemaps/react/${targetPath}") + def jsIntermediateSourceMapsDir = file("$buildDir/intermediates/sourcemaps/react/${targetPath}") + def jsPackagerSourceMapFile = file("$jsIntermediateSourceMapsDir/${bundleAssetName}.packager.map") + def jsCompilerSourceMapFile = file("$jsIntermediateSourceMapsDir/${bundleAssetName}.compiler.map") + def jsOutputSourceMapFile = file("$jsSourceMapsDir/${bundleAssetName}.map") + + // Additional node and packager commandline arguments + def nodeExecutableAndArgs = config.nodeExecutableAndArgs ?: ["node"] + def extraPackagerArgs = config.extraPackagerArgs ?: [] + + def enableHermes = enableHermesForVariant(variant) + + def currentBundleTask = tasks.create( + name: "bundle${targetName}JsAndAssets", + type: Exec) { + group = "react" + description = "bundle JS and assets for ${targetName}." + + // Create dirs if they are not there (e.g. the "clean" task just ran) + doFirst { + jsBundleDir.deleteDir() + jsBundleDir.mkdirs() + resourcesDir.deleteDir() + resourcesDir.mkdirs() + jsIntermediateSourceMapsDir.deleteDir() + jsIntermediateSourceMapsDir.mkdirs() + jsSourceMapsDir.deleteDir() + jsSourceMapsDir.mkdirs() + } + + // Set up inputs and outputs so gradle can cache the result + inputs.files fileTree(dir: reactRoot, excludes: inputExcludes) + outputs.dir(jsBundleDir) + outputs.dir(resourcesDir) + + // Set up the call to the react-native cli + workingDir(reactRoot) + + // Set up dev mode + def devEnabled = !(config."devDisabledIn${targetName}" + || targetName.toLowerCase().contains("release")) + + def extraArgs = extraPackagerArgs; + + if (bundleConfig) { + extraArgs = extraArgs.clone() + extraArgs.add("--config"); + extraArgs.add(bundleConfig); + } + + if (Os.isFamily(Os.FAMILY_WINDOWS)) { + commandLine("cmd", "/c", *nodeExecutableAndArgs, cliPath, bundleCommand, "--platform", "android", "--dev", "${devEnabled}", + "--reset-cache", "--entry-file", entryFile, "--bundle-output", jsBundleFile, "--assets-dest", resourcesDir, + "--sourcemap-output", enableHermes ? jsPackagerSourceMapFile : jsOutputSourceMapFile, *extraArgs) + } else { + commandLine(*nodeExecutableAndArgs, cliPath, bundleCommand, "--platform", "android", "--dev", "${devEnabled}", + "--reset-cache", "--entry-file", entryFile, "--bundle-output", jsBundleFile, "--assets-dest", resourcesDir, + "--sourcemap-output", enableHermes ? jsPackagerSourceMapFile : jsOutputSourceMapFile, *extraArgs) + } + + if (enableHermes) { + doLast { + def hermesFlags; + def hbcTempFile = file("${jsBundleFile}.hbc") + exec { + if (!targetName.toLowerCase().contains("debug")) { + // Can't use ?: since that will also substitute valid empty lists + hermesFlags = config.hermesFlagsRelease + if (hermesFlags == null) hermesFlags = ["-O", "-output-source-map"] + } else { + hermesFlags = config.hermesFlagsDebug + if (hermesFlags == null) hermesFlags = [] + } + commandLine(getHermesCommand(), "-emit-binary", "-out", hbcTempFile, jsBundleFile, *hermesFlags) + } + ant.move( + file: hbcTempFile, + toFile: jsBundleFile + ); + if (hermesFlags.contains("-output-source-map")) { + ant.move( + // Hermes will generate a source map with this exact name + file: "${jsBundleFile}.hbc.map", + tofile: jsCompilerSourceMapFile + ); + exec { + // TODO: set task dependencies for caching + + // Set up the call to the compose-source-maps script + workingDir(reactRoot) + if (Os.isFamily(Os.FAMILY_WINDOWS)) { + commandLine("cmd", "/c", *nodeExecutableAndArgs, composeSourceMapsPath, jsPackagerSourceMapFile, jsCompilerSourceMapFile, "-o", jsOutputSourceMapFile) + } else { + commandLine(*nodeExecutableAndArgs, composeSourceMapsPath, jsPackagerSourceMapFile, jsCompilerSourceMapFile, "-o", jsOutputSourceMapFile) + } + } + } + } + } + + enabled config."bundleIn${targetName}" != null + ? config."bundleIn${targetName}" + : config."bundleIn${variant.buildType.name.capitalize()}" != null + ? config."bundleIn${variant.buildType.name.capitalize()}" + : targetName.toLowerCase().contains("release") + } + + // Expose a minimal interface on the application variant and the task itself: + variant.ext.bundleJsAndAssets = currentBundleTask + currentBundleTask.ext.generatedResFolders = files(resourcesDir).builtBy(currentBundleTask) + currentBundleTask.ext.generatedAssetsFolders = files(jsBundleDir).builtBy(currentBundleTask) + + // registerGeneratedResFolders for Android plugin 3.x + if (variant.respondsTo("registerGeneratedResFolders")) { + variant.registerGeneratedResFolders(currentBundleTask.generatedResFolders) + } else { + variant.registerResGeneratingTask(currentBundleTask) + } + variant.mergeResourcesProvider.get().dependsOn(currentBundleTask) + + // packageApplication for Android plugin 3.x + def packageTask = variant.hasProperty("packageApplication") + ? variant.packageApplicationProvider.get() + : tasks.findByName("package${targetName}") + if (variant.hasProperty("packageLibrary")) { + packageTask = variant.packageLibrary + } + + // pre bundle build task for Android plugin 3.2+ + def buildPreBundleTask = tasks.findByName("build${targetName}PreBundle") + + def resourcesDirConfigValue = config."resourcesDir${targetName}" + if (resourcesDirConfigValue) { + def currentCopyResTask = tasks.create( + name: "copy${targetName}BundledResources", + type: Copy) { + group = "react" + description = "copy bundled resources into custom location for ${targetName}." + + from(resourcesDir) + into(file(resourcesDirConfigValue)) + + dependsOn(currentBundleTask) + + enabled(currentBundleTask.enabled) + } + + packageTask.dependsOn(currentCopyResTask) + if (buildPreBundleTask != null) { + buildPreBundleTask.dependsOn(currentCopyResTask) + } + } + + def currentAssetsCopyTask = tasks.create( + name: "copy${targetName}BundledJs", + type: Copy) { + group = "react" + description = "copy bundled JS into ${targetName}." + + if (config."jsBundleDir${targetName}") { + from(jsBundleDir) + into(file(config."jsBundleDir${targetName}")) + } else { + into ("$buildDir/intermediates") + into ("assets/${targetPath}") { + from(jsBundleDir) + } + + // Workaround for Android Gradle Plugin 3.2+ new asset directory + into ("merged_assets/${variant.name}/merge${targetName}Assets/out") { + from(jsBundleDir) + } + + // Workaround for Android Gradle Plugin 3.4+ new asset directory + into ("merged_assets/${variant.name}/out") { + from(jsBundleDir) + } + } + + // mergeAssets must run first, as it clears the intermediates directory + dependsOn(variant.mergeAssetsProvider.get()) + + enabled(currentBundleTask.enabled) + } + + packageTask.dependsOn(currentAssetsCopyTask) + if (buildPreBundleTask != null) { + buildPreBundleTask.dependsOn(currentAssetsCopyTask) + } + + // Delete the VM related libraries that this build doesn't need. + // The application can manage this manually by setting 'enableVmCleanup: false' + // + // This should really be done by packaging all Hermes releated libs into + // two separate HermesDebug and HermesRelease AARs, but until then we'll + // kludge it by deleting the .so files out of the /transforms/ directory. + def isRelease = !targetName.toLowerCase().contains("debug") + def libDir = "$buildDir/intermediates/transforms/" + def vmSelectionAction = { + fileTree(libDir).matching { + if (enableHermes) { + // For Hermes, delete all the libjsc* files + include "**/libjsc*.so" + + if (isRelease) { + // Reduce size by deleting the debugger/inspector + include '**/libhermes-inspector.so' + include '**/libhermes-executor-debug.so' + } else { + // Release libs take precedence and must be removed + // to allow debugging + include '**/libhermes-executor-release.so' + } + } else { + // For JSC, delete all the libhermes* files + include "**/libhermes*.so" + } + }.visit { details -> + def targetVariant = ".*/transforms/[^/]*/${targetPath}/.*" + def path = details.file.getAbsolutePath().replace(File.separatorChar, '/' as char) + if (path.matches(targetVariant) && details.file.isFile()) { + details.file.delete() + } + } + } + + if (enableVmCleanup) { + def task = tasks.findByName("package${targetName}") + task.doFirst(vmSelectionAction) + } + } +} diff --git a/packages/mobile/android/app/src/main/AndroidManifest.xml b/packages/mobile/android/app/src/main/AndroidManifest.xml index 4727113da69..0c14ec2f6e7 100644 --- a/packages/mobile/android/app/src/main/AndroidManifest.xml +++ b/packages/mobile/android/app/src/main/AndroidManifest.xml @@ -37,7 +37,7 @@ See README(https://goo.gl/l4GJaQ) for more. --> - + diff --git a/packages/mobile/android/app/src/main/assets/custom/LicenseDisclaimer.txt b/packages/mobile/android/app/src/main/assets/custom/LicenseDisclaimer.txt index 31a2ff50d9b..23cd9ba806f 100644 --- a/packages/mobile/android/app/src/main/assets/custom/LicenseDisclaimer.txt +++ b/packages/mobile/android/app/src/main/assets/custom/LicenseDisclaimer.txt @@ -46,7 +46,7 @@ SOFTWARE. ----- -The following software may be included in this product: @ava/babel-plugin-throws-helper, append-transform, caching-transform, call-signature, currently-unhandled, find-cache-dir, last-line-stream, node-modules-regexp, option-chain, require-precompiled, unique-temp-dir. A copy of the source code may be downloaded from https://github.com/avajs/babel-plugin-throws-helper.git (@ava/babel-plugin-throws-helper), https://github.com/jamestalmage/append-transform.git (append-transform), https://github.com/jamestalmage/caching-transform.git (caching-transform), https://github.com/jamestalmage/call-signature.git (call-signature), https://github.com/jamestalmage/currently-unhandled.git (currently-unhandled), https://github.com/avajs/find-cache-dir.git (find-cache-dir), https://github.com/jamestalmage/last-line-stream.git (last-line-stream), https://github.com/jamestalmage/node-modules-regexp.git (node-modules-regexp), https://github.com/avajs/option-chain.git (option-chain), https://github.com/jamestalmage/require-precompiled.git (require-precompiled), https://github.com/jamestalmage/unique-temp-dir.git (unique-temp-dir). This software contains the following license and notice below: +The following software may be included in this product: @ava/babel-plugin-throws-helper, append-transform, caching-transform, call-signature, currently-unhandled, find-cache-dir, last-line-stream, node-modules-regexp, option-chain, require-precompiled, unique-temp-dir. A copy of the source code may be downloaded from https://github.com/avajs/babel-plugin-throws-helper.git (@ava/babel-plugin-throws-helper), https://github.com/jamestalmage/append-transform.git (append-transform), https://github.com/jamestalmage/caching-transform.git (caching-transform), https://github.com/jamestalmage/call-signature.git (call-signature), https://github.com/jamestalmage/currently-unhandled.git (currently-unhandled), https://github.com/jamestalmage/find-cache-dir.git (find-cache-dir), https://github.com/jamestalmage/last-line-stream.git (last-line-stream), https://github.com/jamestalmage/node-modules-regexp.git (node-modules-regexp), https://github.com/avajs/option-chain.git (option-chain), https://github.com/jamestalmage/require-precompiled.git (require-precompiled), https://github.com/jamestalmage/unique-temp-dir.git (unique-temp-dir). This software contains the following license and notice below: The MIT License (MIT) @@ -189,7 +189,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ----- -The following software may be included in this product: @babel/parser, babylon. A copy of the source code may be downloaded from https://github.com/babel/babel/tree/master/packages/babel-parser (@babel/parser), https://github.com/babel/babel/tree/master/packages/babylon (babylon). This software contains the following license and notice below: +The following software may be included in this product: @babel/parser, babylon. A copy of the source code may be downloaded from https://github.com/babel/babel/tree/master/packages/babel-parser (@babel/parser), https://github.com/babel/babylon (babylon). This software contains the following license and notice below: Copyright (C) 2012-2014 by various contributors (see AUTHORS) @@ -584,7 +584,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ----- -The following software may be included in this product: @evocateur/npm-registry-fetch, cacache, figgy-pudding, make-fetch-happen, npm-pick-manifest, ssri. A copy of the source code may be downloaded from https://github.com/evocateur/npm-registry-fetch (@evocateur/npm-registry-fetch), https://github.com/zkat/cacache (cacache), https://github.com/zkat/figgy-pudding (figgy-pudding), https://github.com/zkat/make-fetch-happen (make-fetch-happen), https://github.com/npm/npm-pick-manifest (npm-pick-manifest), https://github.com/zkat/ssri (ssri). This software contains the following license and notice below: +The following software may be included in this product: @evocateur/npm-registry-fetch, cacache, figgy-pudding, make-fetch-happen, npm-pick-manifest, ssri. A copy of the source code may be downloaded from https://github.com/evocateur/npm-registry-fetch (@evocateur/npm-registry-fetch), https://github.com/npm/cacache (cacache), https://github.com/zkat/figgy-pudding (figgy-pudding), https://github.com/zkat/make-fetch-happen (make-fetch-happen), https://github.com/npm/npm-pick-manifest (npm-pick-manifest), https://github.com/zkat/ssri (ssri). This software contains the following license and notice below: ISC License @@ -836,7 +836,7 @@ Apache License ----- -The following software may be included in this product: @firebase/auth, @google-cloud/common, @google-cloud/common-grpc, @google-cloud/firestore, @google-cloud/logging, @google-cloud/monitoring, @google-cloud/paginator, @google-cloud/precise-date, @google-cloud/projectify, @google-cloud/promisify, @google-cloud/pubsub, @google-cloud/storage, @opencensus/core, @opencensus/propagation-stackdriver, @xtuc/long, ascli, bytebuffer, firebase-bolt, futoin-hkdf, gcp-metadata, google-auth-library, google-proto-files, long, protobufjs, spdx-correct, sumchecker, teeny-request, validate-npm-package-license, xcode. A copy of the source code may be downloaded from https://github.com/firebase/firebase-js-sdk.git (@firebase/auth), https://github.com/googleapis/nodejs-common.git (@google-cloud/common), https://github.com/googleapis/nodejs-common-grpc.git (@google-cloud/common-grpc), https://github.com/googleapis/nodejs-firestore.git (@google-cloud/firestore), https://github.com/googleapis/nodejs-logging.git (@google-cloud/logging), https://github.com/googleapis/nodejs-monitoring.git (@google-cloud/monitoring), https://github.com/googleapis/nodejs-paginator.git (@google-cloud/paginator), https://github.com/googleapis/nodejs-precise-date.git (@google-cloud/precise-date), https://github.com/googleapis/nodejs-projectify.git (@google-cloud/projectify), https://github.com/googleapis/nodejs-promisify.git (@google-cloud/promisify), https://github.com/googleapis/nodejs-pubsub.git (@google-cloud/pubsub), https://github.com/googleapis/nodejs-storage.git (@google-cloud/storage), https://github.com/census-instrumentation/opencensus-node.git (@opencensus/core), https://github.com/census-instrumentation/opencensus-node.git (@opencensus/propagation-stackdriver), https://github.com/dcodeIO/long.js.git (@xtuc/long), https://github.com/dcodeIO/ascli.git (ascli), https://github.com/dcodeIO/bytebuffer.js.git (bytebuffer), https://github.com/firebase/bolt.git (firebase-bolt), https://github.com/futoin/util-js-hkdf.git (futoin-hkdf), https://github.com/googleapis/gcp-metadata.git (gcp-metadata), https://github.com/googleapis/google-auth-library-nodejs.git (google-auth-library), https://github.com/googleapis/nodejs-proto-files.git (google-proto-files), https://github.com/dcodeIO/long.js.git (long), https://github.com/dcodeIO/protobuf.js.git (protobufjs), https://github.com/jslicense/spdx-correct.js.git (spdx-correct), git+https://github.com/malept/sumchecker.git (sumchecker), https://github.com/googleapis/teeny-request.git (teeny-request), https://github.com/kemitchell/validate-npm-package-license.js.git (validate-npm-package-license), https://github.com/apache/cordova-node-xcode.git (xcode). This software contains the following license and notice below: +The following software may be included in this product: @firebase/auth, @google-cloud/common, @google-cloud/firestore, @google-cloud/logging, @google-cloud/monitoring, @google-cloud/paginator, @google-cloud/precise-date, @google-cloud/projectify, @google-cloud/promisify, @google-cloud/pubsub, @google-cloud/storage, @opencensus/core, @opencensus/propagation-stackdriver, @xtuc/long, ascli, bytebuffer, firebase-bolt, futoin-hkdf, gaxios, gcp-metadata, google-auth-library, google-proto-files, long, protobufjs, spdx-correct, sumchecker, teeny-request, validate-npm-package-license, xcode. A copy of the source code may be downloaded from https://github.com/firebase/firebase-js-sdk.git (@firebase/auth), https://github.com/googleapis/nodejs-common.git (@google-cloud/common), https://github.com/googleapis/nodejs-firestore.git (@google-cloud/firestore), https://github.com/googleapis/nodejs-logging.git (@google-cloud/logging), https://github.com/googleapis/nodejs-monitoring.git (@google-cloud/monitoring), https://github.com/googleapis/nodejs-paginator.git (@google-cloud/paginator), https://github.com/googleapis/nodejs-precise-date.git (@google-cloud/precise-date), https://github.com/googleapis/nodejs-projectify.git (@google-cloud/projectify), https://github.com/googleapis/nodejs-promisify.git (@google-cloud/promisify), https://github.com/googleapis/nodejs-pubsub.git (@google-cloud/pubsub), https://github.com/googleapis/nodejs-storage.git (@google-cloud/storage), https://github.com/census-instrumentation/opencensus-node.git (@opencensus/core), https://github.com/census-instrumentation/opencensus-node.git (@opencensus/propagation-stackdriver), https://github.com/dcodeIO/long.js.git (@xtuc/long), https://github.com/dcodeIO/ascli.git (ascli), https://github.com/dcodeIO/bytebuffer.js.git (bytebuffer), https://github.com/firebase/bolt.git (firebase-bolt), https://github.com/futoin/util-js-hkdf.git (futoin-hkdf), https://github.com/JustinBeckwith/gaxios.git (gaxios), https://github.com/googleapis/gcp-metadata.git (gcp-metadata), https://github.com/googleapis/google-auth-library-nodejs.git (google-auth-library), https://github.com/googleapis/nodejs-proto-files.git (google-proto-files), https://github.com/dcodeIO/long.js.git (long), https://github.com/dcodeIO/protobuf.js.git (protobufjs), https://github.com/jslicense/spdx-correct.js.git (spdx-correct), git+https://github.com/malept/sumchecker.git (sumchecker), https://github.com/googleapis/teeny-request.git (teeny-request), https://github.com/kemitchell/validate-npm-package-license.js.git (validate-npm-package-license), https://github.com/apache/cordova-node-xcode.git (xcode). This software contains the following license and notice below: Apache License Version 2.0, January 2004 @@ -1772,7 +1772,7 @@ Apache License ----- -The following software may be included in this product: @lerna/add, @lerna/batch-packages, @lerna/bootstrap, @lerna/changed, @lerna/check-working-tree, @lerna/child-process, @lerna/clean, @lerna/cli, @lerna/collect-uncommitted, @lerna/collect-updates, @lerna/command, @lerna/conventional-commits, @lerna/create, @lerna/create-symlink, @lerna/describe-ref, @lerna/diff, @lerna/exec, @lerna/filter-options, @lerna/filter-packages, @lerna/get-npm-exec-opts, @lerna/get-packed, @lerna/github-client, @lerna/gitlab-client, @lerna/global-options, @lerna/has-npm-version, @lerna/import, @lerna/init, @lerna/link, @lerna/list, @lerna/listable, @lerna/log-packed, @lerna/npm-conf, @lerna/npm-dist-tag, @lerna/npm-install, @lerna/npm-publish, @lerna/npm-run-script, @lerna/otplease, @lerna/output, @lerna/pack-directory, @lerna/package, @lerna/package-graph, @lerna/prerelease-id-from-version, @lerna/project, @lerna/prompt, @lerna/publish, @lerna/pulse-till-done, @lerna/query-graph, @lerna/resolve-symlink, @lerna/rimraf-dir, @lerna/run, @lerna/run-lifecycle, @lerna/run-parallel-batches, @lerna/run-topologically, @lerna/symlink-binary, @lerna/symlink-dependencies, @lerna/timer, @lerna/validation-error, @lerna/version, @lerna/write-log-file, lerna. A copy of the source code may be downloaded from git+https://github.com/lerna/lerna.git (@lerna/add), git+https://github.com/lerna/lerna.git (@lerna/batch-packages), git+https://github.com/lerna/lerna.git (@lerna/bootstrap), git+https://github.com/lerna/lerna.git (@lerna/changed), git+https://github.com/lerna/lerna.git (@lerna/check-working-tree), git+https://github.com/lerna/lerna.git (@lerna/child-process), git+https://github.com/lerna/lerna.git (@lerna/clean), git+https://github.com/lerna/lerna.git (@lerna/cli), git+https://github.com/lerna/lerna.git (@lerna/collect-uncommitted), git+https://github.com/lerna/lerna.git (@lerna/collect-updates), git+https://github.com/lerna/lerna.git (@lerna/command), git+https://github.com/lerna/lerna.git (@lerna/conventional-commits), git+https://github.com/lerna/lerna.git (@lerna/create), git+https://github.com/lerna/lerna.git (@lerna/create-symlink), git+https://github.com/lerna/lerna.git (@lerna/describe-ref), git+https://github.com/lerna/lerna.git (@lerna/diff), git+https://github.com/lerna/lerna.git (@lerna/exec), git+https://github.com/lerna/lerna.git (@lerna/filter-options), git+https://github.com/lerna/lerna.git (@lerna/filter-packages), git+https://github.com/lerna/lerna.git (@lerna/get-npm-exec-opts), git+https://github.com/lerna/lerna.git (@lerna/get-packed), git+https://github.com/lerna/lerna.git (@lerna/github-client), git+https://gitlab.com/lerna/lerna.git (@lerna/gitlab-client), git+https://github.com/lerna/lerna.git (@lerna/global-options), git+https://github.com/lerna/lerna.git (@lerna/has-npm-version), git+https://github.com/lerna/lerna.git (@lerna/import), git+https://github.com/lerna/lerna.git (@lerna/init), git+https://github.com/lerna/lerna.git (@lerna/link), git+https://github.com/lerna/lerna.git (@lerna/list), git+https://github.com/lerna/lerna.git (@lerna/listable), git+https://github.com/lerna/lerna.git (@lerna/log-packed), git+https://github.com/lerna/lerna.git (@lerna/npm-conf), git+https://github.com/lerna/lerna.git (@lerna/npm-dist-tag), git+https://github.com/lerna/lerna.git (@lerna/npm-install), git+https://github.com/lerna/lerna.git (@lerna/npm-publish), git+https://github.com/lerna/lerna.git (@lerna/npm-run-script), git+https://github.com/lerna/lerna.git (@lerna/otplease), git+https://github.com/lerna/lerna.git (@lerna/output), git+https://github.com/lerna/lerna.git (@lerna/pack-directory), git+https://github.com/lerna/lerna.git (@lerna/package), git+https://github.com/lerna/lerna.git (@lerna/package-graph), git+https://github.com/lerna/lerna.git (@lerna/prerelease-id-from-version), git+https://github.com/lerna/lerna.git (@lerna/project), git+https://github.com/lerna/lerna.git (@lerna/prompt), git+https://github.com/lerna/lerna.git (@lerna/publish), git+https://github.com/lerna/lerna.git (@lerna/pulse-till-done), git+https://github.com/lerna/lerna.git (@lerna/query-graph), git+https://github.com/lerna/lerna.git (@lerna/resolve-symlink), git+https://github.com/lerna/lerna.git (@lerna/rimraf-dir), git+https://github.com/lerna/lerna.git (@lerna/run), git+https://github.com/lerna/lerna.git (@lerna/run-lifecycle), git+https://github.com/lerna/lerna.git (@lerna/run-parallel-batches), git+https://github.com/lerna/lerna.git (@lerna/run-topologically), git+https://github.com/lerna/lerna.git (@lerna/symlink-binary), git+https://github.com/lerna/lerna.git (@lerna/symlink-dependencies), git+https://github.com/lerna/lerna.git (@lerna/timer), git+https://github.com/lerna/lerna.git (@lerna/validation-error), git+https://github.com/lerna/lerna.git (@lerna/version), git+https://github.com/lerna/lerna.git (@lerna/write-log-file), git+https://github.com/lerna/lerna.git (lerna). This software contains the following license and notice below: +The following software may be included in this product: @lerna/add, @lerna/bootstrap, @lerna/changed, @lerna/check-working-tree, @lerna/child-process, @lerna/clean, @lerna/cli, @lerna/collect-uncommitted, @lerna/collect-updates, @lerna/command, @lerna/conventional-commits, @lerna/create, @lerna/create-symlink, @lerna/describe-ref, @lerna/diff, @lerna/exec, @lerna/filter-options, @lerna/filter-packages, @lerna/get-npm-exec-opts, @lerna/get-packed, @lerna/github-client, @lerna/gitlab-client, @lerna/global-options, @lerna/has-npm-version, @lerna/import, @lerna/init, @lerna/link, @lerna/list, @lerna/listable, @lerna/log-packed, @lerna/npm-conf, @lerna/npm-dist-tag, @lerna/npm-install, @lerna/npm-publish, @lerna/npm-run-script, @lerna/otplease, @lerna/output, @lerna/pack-directory, @lerna/package, @lerna/package-graph, @lerna/prerelease-id-from-version, @lerna/project, @lerna/prompt, @lerna/publish, @lerna/pulse-till-done, @lerna/query-graph, @lerna/resolve-symlink, @lerna/rimraf-dir, @lerna/run, @lerna/run-lifecycle, @lerna/run-topologically, @lerna/symlink-binary, @lerna/symlink-dependencies, @lerna/timer, @lerna/validation-error, @lerna/version, @lerna/write-log-file, lerna. A copy of the source code may be downloaded from git+https://github.com/lerna/lerna.git (@lerna/add), git+https://github.com/lerna/lerna.git (@lerna/bootstrap), git+https://github.com/lerna/lerna.git (@lerna/changed), git+https://github.com/lerna/lerna.git (@lerna/check-working-tree), git+https://github.com/lerna/lerna.git (@lerna/child-process), git+https://github.com/lerna/lerna.git (@lerna/clean), git+https://github.com/lerna/lerna.git (@lerna/cli), git+https://github.com/lerna/lerna.git (@lerna/collect-uncommitted), git+https://github.com/lerna/lerna.git (@lerna/collect-updates), git+https://github.com/lerna/lerna.git (@lerna/command), git+https://github.com/lerna/lerna.git (@lerna/conventional-commits), git+https://github.com/lerna/lerna.git (@lerna/create), git+https://github.com/lerna/lerna.git (@lerna/create-symlink), git+https://github.com/lerna/lerna.git (@lerna/describe-ref), git+https://github.com/lerna/lerna.git (@lerna/diff), git+https://github.com/lerna/lerna.git (@lerna/exec), git+https://github.com/lerna/lerna.git (@lerna/filter-options), git+https://github.com/lerna/lerna.git (@lerna/filter-packages), git+https://github.com/lerna/lerna.git (@lerna/get-npm-exec-opts), git+https://github.com/lerna/lerna.git (@lerna/get-packed), git+https://github.com/lerna/lerna.git (@lerna/github-client), git+https://gitlab.com/lerna/lerna.git (@lerna/gitlab-client), git+https://github.com/lerna/lerna.git (@lerna/global-options), git+https://github.com/lerna/lerna.git (@lerna/has-npm-version), git+https://github.com/lerna/lerna.git (@lerna/import), git+https://github.com/lerna/lerna.git (@lerna/init), git+https://github.com/lerna/lerna.git (@lerna/link), git+https://github.com/lerna/lerna.git (@lerna/list), git+https://github.com/lerna/lerna.git (@lerna/listable), git+https://github.com/lerna/lerna.git (@lerna/log-packed), git+https://github.com/lerna/lerna.git (@lerna/npm-conf), git+https://github.com/lerna/lerna.git (@lerna/npm-dist-tag), git+https://github.com/lerna/lerna.git (@lerna/npm-install), git+https://github.com/lerna/lerna.git (@lerna/npm-publish), git+https://github.com/lerna/lerna.git (@lerna/npm-run-script), git+https://github.com/lerna/lerna.git (@lerna/otplease), git+https://github.com/lerna/lerna.git (@lerna/output), git+https://github.com/lerna/lerna.git (@lerna/pack-directory), git+https://github.com/lerna/lerna.git (@lerna/package), git+https://github.com/lerna/lerna.git (@lerna/package-graph), git+https://github.com/lerna/lerna.git (@lerna/prerelease-id-from-version), git+https://github.com/lerna/lerna.git (@lerna/project), git+https://github.com/lerna/lerna.git (@lerna/prompt), git+https://github.com/lerna/lerna.git (@lerna/publish), git+https://github.com/lerna/lerna.git (@lerna/pulse-till-done), git+https://github.com/lerna/lerna.git (@lerna/query-graph), git+https://github.com/lerna/lerna.git (@lerna/resolve-symlink), git+https://github.com/lerna/lerna.git (@lerna/rimraf-dir), git+https://github.com/lerna/lerna.git (@lerna/run), git+https://github.com/lerna/lerna.git (@lerna/run-lifecycle), git+https://github.com/lerna/lerna.git (@lerna/run-topologically), git+https://github.com/lerna/lerna.git (@lerna/symlink-binary), git+https://github.com/lerna/lerna.git (@lerna/symlink-dependencies), git+https://github.com/lerna/lerna.git (@lerna/timer), git+https://github.com/lerna/lerna.git (@lerna/validation-error), git+https://github.com/lerna/lerna.git (@lerna/version), git+https://github.com/lerna/lerna.git (@lerna/write-log-file), git+https://github.com/lerna/lerna.git (lerna). This software contains the following license and notice below: Copyright (c) 2015-present Lerna Contributors @@ -1932,6 +1932,18 @@ THE SOFTWARE. ----- +The following software may be included in this product: @octokit/types. A copy of the source code may be downloaded from https://github.com/octokit/types.ts. This software contains the following license and notice below: + +MIT License Copyright (c) 2019 Octokit contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +----- + The following software may be included in this product: @protobufjs/aspromise, @protobufjs/base64, @protobufjs/codegen, @protobufjs/eventemitter, @protobufjs/fetch, @protobufjs/float, @protobufjs/inquire, @protobufjs/path, @protobufjs/pool, @protobufjs/utf8. A copy of the source code may be downloaded from https://github.com/dcodeIO/protobuf.js.git (@protobufjs/aspromise), https://github.com/dcodeIO/protobuf.js.git (@protobufjs/base64), https://github.com/dcodeIO/protobuf.js.git (@protobufjs/codegen), https://github.com/dcodeIO/protobuf.js.git (@protobufjs/eventemitter), https://github.com/dcodeIO/protobuf.js.git (@protobufjs/fetch), https://github.com/dcodeIO/protobuf.js.git (@protobufjs/float), https://github.com/dcodeIO/protobuf.js.git (@protobufjs/inquire), https://github.com/dcodeIO/protobuf.js.git (@protobufjs/path), https://github.com/dcodeIO/protobuf.js.git (@protobufjs/pool), https://github.com/dcodeIO/protobuf.js.git (@protobufjs/utf8). This software contains the following license and notice below: Copyright (c) 2016, Daniel Wirtz All rights reserved. @@ -2589,7 +2601,7 @@ SOFTWARE. ----- -The following software may be included in this product: @types/accepts, @types/airtable, @types/anymatch, @types/async-polling, @types/babel__core, @types/babel__generator, @types/babel__template, @types/babel__traverse, @types/babel-types, @types/babylon, @types/bignumber.js, @types/bip32, @types/bip39, @types/bn.js, @types/body-parser, @types/bytebuffer, @types/caseless, @types/chai, @types/cheerio, @types/cli-table, @types/connect, @types/cookiejar, @types/cookies, @types/cors, @types/country-data, @types/debug, @types/dotenv, @types/duplexify, @types/elliptic, @types/enzyme, @types/enzyme-adapter-react-16, @types/eth-lightwallet, @types/ethereum-protocol, @types/ethereumjs-util, @types/events, @types/express, @types/express-serve-static-core, @types/fs-capacitor, @types/fs-extra, @types/glob, @types/google-libphonenumber, @types/graphql, @types/graphql-upload, @types/hdkey, @types/hoist-non-react-statics, @types/http-assert, @types/i18next, @types/invariant, @types/is-glob, @types/isomorphic-fetch, @types/istanbul-lib-coverage, @types/istanbul-lib-report, @types/istanbul-reports, @types/jest, @types/jest-diff, @types/koa, @types/koa-compose, @types/lodash, @types/lodash.zipobject, @types/long, @types/mailgun-js, @types/mathjs, @types/mime, @types/minimatch, @types/mkdirp, @types/mocha, @types/next, @types/next-server, @types/node, @types/node-fetch, @types/nodemailer, @types/normalize-package-data, @types/p-defer, @types/prettier, @types/prompts, @types/prop-types, @types/q, @types/qs, @types/range-parser, @types/react, @types/react-autosuggest, @types/react-css-modules, @types/react-google-recaptcha, @types/react-loadable, @types/react-native, @types/react-native-autocomplete-input, @types/react-native-fs, @types/react-native-keep-awake, @types/react-redux, @types/react-test-renderer, @types/redux-mock-store, @types/request, @types/resolve, @types/serve-static, @types/solidity-parser-antlr, @types/source-list-map, @types/stack-utils, @types/superagent, @types/supertest, @types/tapable, @types/tough-cookie, @types/twilio, @types/uglify-js, @types/underscore, @types/utf8, @types/uuid-js, @types/web3, @types/web3-provider-engine, @types/webpack, @types/webpack-sources, @types/ws, @types/yargs, @types/yargs-parser, @types/zen-observable. A copy of the source code may be downloaded from https://www.github.com/DefinitelyTyped/DefinitelyTyped.git (@types/accepts), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/airtable), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/anymatch), https://www.github.com/DefinitelyTyped/DefinitelyTyped.git (@types/async-polling), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/babel__core), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/babel__generator), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/babel__template), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/babel__traverse), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/babel-types), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/babylon), https://github.com/MikeMcl/bignumber.js/ (@types/bignumber.js), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/bip32), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/bip39), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/bn.js), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/body-parser), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/bytebuffer), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/caseless), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/chai), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/cheerio), https://github.com/DefinitelyTyped/DefinitelyTyped.git.git (@types/cli-table), https://www.github.com/DefinitelyTyped/DefinitelyTyped.git (@types/connect), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/cookiejar), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/cookies), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/cors), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/country-data), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/debug), https://www.github.com/DefinitelyTyped/DefinitelyTyped.git (@types/dotenv), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/duplexify), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/elliptic), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/enzyme), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/enzyme-adapter-react-16), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/eth-lightwallet), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/ethereum-protocol), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/ethereumjs-util), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/events), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/express), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/express-serve-static-core), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/fs-capacitor), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/fs-extra), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/glob), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/google-libphonenumber), https://github.com/graphql/graphql-js (@types/graphql), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/graphql-upload), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/hdkey), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/hoist-non-react-statics), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/http-assert), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/i18next), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/invariant), https://www.github.com/DefinitelyTyped/DefinitelyTyped.git (@types/is-glob), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/isomorphic-fetch), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/istanbul-lib-coverage), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/istanbul-lib-report), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/istanbul-reports), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/jest), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/jest-diff), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/koa), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/koa-compose), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/lodash), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/lodash.zipobject), https://www.github.com/DefinitelyTyped/DefinitelyTyped.git (@types/long), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/mailgun-js), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/mathjs), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/mime), https://www.github.com/DefinitelyTyped/DefinitelyTyped.git (@types/minimatch), https://www.github.com/DefinitelyTyped/DefinitelyTyped.git (@types/mkdirp), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/mocha), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/next), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/next-server), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/node), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/node-fetch), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/nodemailer), https://www.github.com/DefinitelyTyped/DefinitelyTyped.git (@types/normalize-package-data), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/p-defer), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/prettier), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/prompts), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/prop-types), https://www.github.com/DefinitelyTyped/DefinitelyTyped.git (@types/q), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/qs), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/range-parser), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/react), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/react-autosuggest), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/react-css-modules), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/react-google-recaptcha), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/react-loadable), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/react-native), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/react-native-autocomplete-input), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/react-native-fs), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/react-native-keep-awake), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/react-redux), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/react-test-renderer), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/redux-mock-store), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/request), https://github.com/DefinitelyTyped/DefinitelyTyped.git.git (@types/resolve), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/serve-static), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/solidity-parser-antlr), https://www.github.com/DefinitelyTyped/DefinitelyTyped.git (@types/source-list-map), https://www.github.com/DefinitelyTyped/DefinitelyTyped.git (@types/stack-utils), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/superagent), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/supertest), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/tapable), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/tough-cookie), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/twilio), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/uglify-js), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/underscore), https://www.github.com/DefinitelyTyped/DefinitelyTyped.git (@types/utf8), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/uuid-js), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/web3), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/web3-provider-engine), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/webpack), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/webpack-sources), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/ws), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/yargs), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/yargs-parser), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/zen-observable). This software contains the following license and notice below: +The following software may be included in this product: @types/accepts, @types/airtable, @types/anymatch, @types/async-polling, @types/babel__core, @types/babel__generator, @types/babel__template, @types/babel__traverse, @types/babel-types, @types/babylon, @types/bignumber.js, @types/bip32, @types/bip39, @types/bn.js, @types/body-parser, @types/bytebuffer, @types/caseless, @types/chai, @types/cheerio, @types/cli-table, @types/connect, @types/cookiejar, @types/cookies, @types/cors, @types/country-data, @types/debug, @types/dotenv, @types/duplexify, @types/elliptic, @types/enzyme, @types/enzyme-adapter-react-16, @types/eth-lightwallet, @types/ethereum-protocol, @types/ethereumjs-util, @types/events, @types/express, @types/express-serve-static-core, @types/fs-capacitor, @types/fs-extra, @types/glob, @types/google-libphonenumber, @types/graphql, @types/graphql-upload, @types/hdkey, @types/hoist-non-react-statics, @types/http-assert, @types/i18next, @types/invariant, @types/is-glob, @types/isomorphic-fetch, @types/istanbul-lib-coverage, @types/istanbul-lib-report, @types/istanbul-reports, @types/jest, @types/jest-diff, @types/koa, @types/koa-compose, @types/lodash, @types/lodash.zipobject, @types/long, @types/mailgun-js, @types/mathjs, @types/mime, @types/minimatch, @types/mkdirp, @types/mocha, @types/next, @types/next-server, @types/node, @types/node-fetch, @types/nodemailer, @types/normalize-package-data, @types/p-defer, @types/prettier, @types/prompts, @types/prop-types, @types/q, @types/qs, @types/range-parser, @types/react, @types/react-autosuggest, @types/react-css-modules, @types/react-google-recaptcha, @types/react-loadable, @types/react-native, @types/react-native-autocomplete-input, @types/react-native-fs, @types/react-native-keep-awake, @types/react-redux, @types/react-test-renderer, @types/redux-mock-store, @types/request, @types/resolve, @types/serve-static, @types/solidity-parser-antlr, @types/source-list-map, @types/stack-utils, @types/superagent, @types/supertest, @types/tapable, @types/tough-cookie, @types/twilio, @types/uglify-js, @types/underscore, @types/utf8, @types/uuid-js, @types/web3, @types/web3-provider-engine, @types/webpack, @types/webpack-sources, @types/ws, @types/yargs, @types/yargs-parser, @types/zen-observable. A copy of the source code may be downloaded from https://www.github.com/DefinitelyTyped/DefinitelyTyped.git (@types/accepts), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/airtable), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/anymatch), https://www.github.com/DefinitelyTyped/DefinitelyTyped.git (@types/async-polling), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/babel__core), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/babel__generator), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/babel__template), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/babel__traverse), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/babel-types), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/babylon), https://github.com/MikeMcl/bignumber.js/ (@types/bignumber.js), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/bip32), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/bip39), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/bn.js), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/body-parser), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/bytebuffer), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/caseless), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/chai), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/cheerio), https://github.com/DefinitelyTyped/DefinitelyTyped.git.git (@types/cli-table), https://www.github.com/DefinitelyTyped/DefinitelyTyped.git (@types/connect), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/cookiejar), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/cookies), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/cors), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/country-data), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/debug), https://www.github.com/DefinitelyTyped/DefinitelyTyped.git (@types/dotenv), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/duplexify), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/elliptic), https://www.github.com/DefinitelyTyped/DefinitelyTyped.git (@types/enzyme), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/enzyme-adapter-react-16), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/eth-lightwallet), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/ethereum-protocol), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/ethereumjs-util), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/events), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/express), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/express-serve-static-core), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/fs-capacitor), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/fs-extra), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/glob), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/google-libphonenumber), https://github.com/graphql/graphql-js (@types/graphql), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/graphql-upload), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/hdkey), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/hoist-non-react-statics), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/http-assert), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/i18next), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/invariant), https://www.github.com/DefinitelyTyped/DefinitelyTyped.git (@types/is-glob), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/isomorphic-fetch), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/istanbul-lib-coverage), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/istanbul-lib-report), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/istanbul-reports), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/jest), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/jest-diff), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/koa), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/koa-compose), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/lodash), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/lodash.zipobject), https://www.github.com/DefinitelyTyped/DefinitelyTyped.git (@types/long), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/mailgun-js), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/mathjs), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/mime), https://www.github.com/DefinitelyTyped/DefinitelyTyped.git (@types/minimatch), https://www.github.com/DefinitelyTyped/DefinitelyTyped.git (@types/mkdirp), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/mocha), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/next), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/next-server), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/node), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/node-fetch), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/nodemailer), https://www.github.com/DefinitelyTyped/DefinitelyTyped.git (@types/normalize-package-data), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/p-defer), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/prettier), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/prompts), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/prop-types), https://www.github.com/DefinitelyTyped/DefinitelyTyped.git (@types/q), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/qs), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/range-parser), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/react), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/react-autosuggest), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/react-css-modules), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/react-google-recaptcha), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/react-loadable), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/react-native), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/react-native-autocomplete-input), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/react-native-fs), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/react-native-keep-awake), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/react-redux), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/react-test-renderer), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/redux-mock-store), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/request), https://github.com/DefinitelyTyped/DefinitelyTyped.git.git (@types/resolve), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/serve-static), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/solidity-parser-antlr), https://www.github.com/DefinitelyTyped/DefinitelyTyped.git (@types/source-list-map), https://www.github.com/DefinitelyTyped/DefinitelyTyped.git (@types/stack-utils), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/superagent), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/supertest), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/tapable), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/tough-cookie), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/twilio), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/uglify-js), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/underscore), https://www.github.com/DefinitelyTyped/DefinitelyTyped.git (@types/utf8), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/uuid-js), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/web3), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/web3-provider-engine), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/webpack), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/webpack-sources), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/ws), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/yargs), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/yargs-parser), https://github.com/DefinitelyTyped/DefinitelyTyped.git (@types/zen-observable). This software contains the following license and notice below: MIT License @@ -4017,7 +4029,7 @@ THE SOFTWARE. ----- -The following software may be included in this product: acorn-jsx. A copy of the source code may be downloaded from https://github.com/RReverser/acorn-jsx. This software contains the following license and notice below: +The following software may be included in this product: acorn-jsx. A copy of the source code may be downloaded from https://github.com/acornjs/acorn-jsx. This software contains the following license and notice below: Copyright (C) 2012-2017 by Ingvar Stepanyan @@ -4224,7 +4236,7 @@ SOFTWARE. ----- -The following software may be included in this product: align-text, ansi-wrap, assign-symbols, center-align, contains-path, define-property, dotdir-regex, es6-template-regex, extglob, glob-base, glob-fs, glob-fs-dotfiles, is-accessor-descriptor, is-data-descriptor, is-dotdir, is-equal-shallow, is-extendable, is-invalid-path, is-windows, lazy-cache, noncharacters, object-visit, parse-gitignore, parse-glob, pascalcase, plugin-error, right-align, shallow-clone, unc-path-regex, window-size. A copy of the source code may be downloaded from git://github.com/jonschlinkert/align-text.git (align-text), https://github.com/jonschlinkert/ansi-wrap.git (ansi-wrap), https://github.com/jonschlinkert/assign-symbols.git (assign-symbols), https://github.com/jonschlinkert/center-align.git (center-align), https://github.com/jonschlinkert/contains-path.git (contains-path), https://github.com/jonschlinkert/define-property.git (define-property), https://github.com/regexps/dotdir-regex.git (dotdir-regex), https://github.com/jonschlinkert/es6-template-regex.git (es6-template-regex), git://github.com/jonschlinkert/extglob.git (extglob), git://github.com/jonschlinkert/glob-base.git (glob-base), https://github.com/jonschlinkert/glob-fs.git (glob-fs), https://github.com/jonschlinkert/glob-fs-dotfiles.git (glob-fs-dotfiles), https://github.com/jonschlinkert/is-accessor-descriptor.git (is-accessor-descriptor), https://github.com/jonschlinkert/is-data-descriptor.git (is-data-descriptor), https://github.com/jonschlinkert/is-dotdir.git (is-dotdir), git://github.com/jonschlinkert/is-equal-shallow.git (is-equal-shallow), https://github.com/jonschlinkert/is-extendable.git (is-extendable), git://github.com/jonschlinkert/is-invalid-path.git (is-invalid-path), https://github.com/jonschlinkert/is-windows.git (is-windows), git://github.com/jonschlinkert/lazy-cache.git (lazy-cache), git://github.com/jonschlinkert/noncharacters.git (noncharacters), https://github.com/jonschlinkert/object-visit.git (object-visit), https://github.com/jonschlinkert/parse-gitignore.git (parse-gitignore), https://github.com/jonschlinkert/parse-glob.git (parse-glob), https://github.com/jonschlinkert/pascalcase.git (pascalcase), git://github.com/jonschlinkert/plugin-error.git (plugin-error), git://github.com/jonschlinkert/right-align.git (right-align), https://github.com/jonschlinkert/shallow-clone.git (shallow-clone), https://github.com/regexhq/unc-path-regex.git (unc-path-regex), https://github.com/jonschlinkert/window-size.git (window-size). This software contains the following license and notice below: +The following software may be included in this product: align-text, ansi-wrap, assign-symbols, center-align, contains-path, define-property, dotdir-regex, es6-template-regex, extglob, glob-base, glob-fs, glob-fs-dotfiles, is-accessor-descriptor, is-data-descriptor, is-dotdir, is-equal-shallow, is-extendable, is-invalid-path, is-windows, lazy-cache, noncharacters, object-visit, parse-gitignore, parse-glob, pascalcase, plugin-error, right-align, shallow-clone, unc-path-regex, window-size. A copy of the source code may be downloaded from git://github.com/jonschlinkert/align-text.git (align-text), https://github.com/jonschlinkert/ansi-wrap.git (ansi-wrap), https://github.com/jonschlinkert/assign-symbols.git (assign-symbols), https://github.com/jonschlinkert/center-align.git (center-align), https://github.com/jonschlinkert/contains-path.git (contains-path), https://github.com/jonschlinkert/define-property.git (define-property), https://github.com/regexps/dotdir-regex.git (dotdir-regex), https://github.com/jonschlinkert/es6-template-regex.git (es6-template-regex), git://github.com/jonschlinkert/extglob.git (extglob), git://github.com/jonschlinkert/glob-base.git (glob-base), https://github.com/jonschlinkert/glob-fs.git (glob-fs), https://github.com/jonschlinkert/glob-fs-dotfiles.git (glob-fs-dotfiles), https://github.com/jonschlinkert/is-accessor-descriptor.git (is-accessor-descriptor), https://github.com/jonschlinkert/is-data-descriptor.git (is-data-descriptor), https://github.com/jonschlinkert/is-dotdir.git (is-dotdir), git://github.com/jonschlinkert/is-equal-shallow.git (is-equal-shallow), https://github.com/jonschlinkert/is-extendable.git (is-extendable), git://github.com/jonschlinkert/is-invalid-path.git (is-invalid-path), https://github.com/jonschlinkert/is-windows.git (is-windows), https://github.com/jonschlinkert/lazy-cache.git (lazy-cache), git://github.com/jonschlinkert/noncharacters.git (noncharacters), https://github.com/jonschlinkert/object-visit.git (object-visit), https://github.com/jonschlinkert/parse-gitignore.git (parse-gitignore), https://github.com/jonschlinkert/parse-glob.git (parse-glob), https://github.com/jonschlinkert/pascalcase.git (pascalcase), git://github.com/jonschlinkert/plugin-error.git (plugin-error), git://github.com/jonschlinkert/right-align.git (right-align), https://github.com/jonschlinkert/shallow-clone.git (shallow-clone), https://github.com/regexhq/unc-path-regex.git (unc-path-regex), https://github.com/jonschlinkert/window-size.git (window-size). This software contains the following license and notice below: The MIT License (MIT) @@ -4570,7 +4582,7 @@ The following software may be included in this product: ansi-colors. A copy of t The MIT License (MIT) -Copyright (c) 2015-present, Brian Woodward. +Copyright (c) 2015-2017, Brian Woodward. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -4596,7 +4608,7 @@ The following software may be included in this product: ansi-colors. A copy of t The MIT License (MIT) -Copyright (c) 2015-2017, Brian Woodward. +Copyright (c) 2015-present, Brian Woodward. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -5118,7 +5130,7 @@ OTHER DEALINGS IN THE SOFTWARE. ----- -The following software may be included in this product: archy, array-map, array-reduce, atob-lite, btoa-lite, buffer-equal, camelize, concat-map, dasherize, deep-equal, defined, ent, fast-json-stable-stringify, github-from-package, is-typedarray, json-stable-stringify, json-stable-stringify-without-jsonify, minimist, object-inspect, path-browserify, resolve, resumer, safe-regex, semver-compare, snakeize, text-table, tty-browserify, vm-browserify, wordwrap. A copy of the source code may be downloaded from http://github.com/substack/node-archy.git (archy), git://github.com/substack/array-map.git (array-map), git://github.com/substack/array-reduce.git (array-reduce), git://github.com/hughsk/atob-lite.git (atob-lite), git://github.com/hughsk/btoa-lite.git (btoa-lite), git://github.com/substack/node-buffer-equal.git (buffer-equal), git://github.com/substack/camelize.git (camelize), git://github.com/substack/node-concat-map.git (concat-map), git://github.com/shahata/dasherize.git (dasherize), http://github.com/substack/node-deep-equal.git (deep-equal), git://github.com/substack/defined.git (defined), https://github.com/substack/node-ent.git (ent), git://github.com/epoberezkin/fast-json-stable-stringify.git (fast-json-stable-stringify), git://github.com/substack/github-from-package.git (github-from-package), git://github.com/hughsk/is-typedarray.git (is-typedarray), git://github.com/substack/json-stable-stringify.git (json-stable-stringify), git://github.com/samn/json-stable-stringify.git (json-stable-stringify-without-jsonify), git://github.com/substack/minimist.git (minimist), git://github.com/substack/object-inspect.git (object-inspect), git://github.com/browserify/path-browserify.git (path-browserify), git://github.com/substack/node-resolve.git (resolve), git://github.com/substack/resumer.git (resumer), git://github.com/substack/safe-regex.git (safe-regex), git://github.com/substack/semver-compare.git (semver-compare), git://github.com/nathan7/snakeize.git (snakeize), git://github.com/substack/text-table.git (text-table), git://github.com/substack/tty-browserify.git (tty-browserify), http://github.com/substack/vm-browserify.git (vm-browserify), git://github.com/substack/node-wordwrap.git (wordwrap). This software contains the following license and notice below: +The following software may be included in this product: archy, array-map, array-reduce, atob-lite, btoa-lite, buffer-equal, camelize, concat-map, dasherize, deep-equal, defined, ent, fast-json-stable-stringify, github-from-package, is-typedarray, json-stable-stringify, json-stable-stringify-without-jsonify, minimist, object-inspect, path-browserify, resolve, resumer, safe-regex, semver-compare, snakeize, text-table, tty-browserify, vm-browserify, wordwrap. A copy of the source code may be downloaded from http://github.com/substack/node-archy.git (archy), git://github.com/substack/array-map.git (array-map), git://github.com/substack/array-reduce.git (array-reduce), git://github.com/hughsk/atob-lite.git (atob-lite), git://github.com/hughsk/btoa-lite.git (btoa-lite), git://github.com/substack/node-buffer-equal.git (buffer-equal), git://github.com/substack/camelize.git (camelize), git://github.com/substack/node-concat-map.git (concat-map), git://github.com/shahata/dasherize.git (dasherize), http://github.com/substack/node-deep-equal.git (deep-equal), git://github.com/substack/defined.git (defined), https://github.com/substack/node-ent.git (ent), git://github.com/epoberezkin/fast-json-stable-stringify.git (fast-json-stable-stringify), git://github.com/substack/github-from-package.git (github-from-package), git://github.com/hughsk/is-typedarray.git (is-typedarray), git://github.com/substack/json-stable-stringify.git (json-stable-stringify), git://github.com/samn/json-stable-stringify.git (json-stable-stringify-without-jsonify), git://github.com/substack/minimist.git (minimist), git://github.com/substack/object-inspect.git (object-inspect), git://github.com/substack/path-browserify.git (path-browserify), git://github.com/browserify/resolve.git (resolve), git://github.com/substack/resumer.git (resumer), git://github.com/substack/safe-regex.git (safe-regex), git://github.com/substack/semver-compare.git (semver-compare), git://github.com/nathan7/snakeize.git (snakeize), git://github.com/substack/text-table.git (text-table), git://github.com/substack/tty-browserify.git (tty-browserify), http://github.com/substack/vm-browserify.git (vm-browserify), git://github.com/substack/node-wordwrap.git (wordwrap). This software contains the following license and notice below: This software is released under the MIT license: @@ -5264,40 +5276,11 @@ SOFTWARE. ----- -The following software may be included in this product: arr-diff. A copy of the source code may be downloaded from https://github.com/jonschlinkert/arr-diff.git. This software contains the following license and notice below: +The following software may be included in this product: arr-diff, arr-union, array-unique, extend-shallow, get-value, is-extglob, is-glob, is-number, is-primitive, isobject, longest, micromatch, mixin-object, object.omit, parse-filepath, relative, set-value, write. A copy of the source code may be downloaded from https://github.com/jonschlinkert/arr-diff.git (arr-diff), git://github.com/jonschlinkert/arr-union.git (arr-union), git://github.com/jonschlinkert/array-unique.git (array-unique), git://github.com/jonschlinkert/extend-shallow.git (extend-shallow), https://github.com/jonschlinkert/get-value.git (get-value), https://github.com/jonschlinkert/is-extglob.git (is-extglob), https://github.com/jonschlinkert/is-glob.git (is-glob), https://github.com/jonschlinkert/is-number.git (is-number), git://github.com/jonschlinkert/is-primitive.git (is-primitive), git://github.com/jonschlinkert/isobject.git (isobject), https://github.com/jonschlinkert/longest.git (longest), https://github.com/jonschlinkert/micromatch.git (micromatch), https://github.com/jonschlinkert/mixin-object.git (mixin-object), git://github.com/jonschlinkert/object.omit.git (object.omit), https://github.com/jonschlinkert/parse-filepath.git (parse-filepath), https://github.com/jonschlinkert/relative.git (relative), git://github.com/jonschlinkert/set-value.git (set-value), https://github.com/jonschlinkert/write.git (write). This software contains the following license and notice below: The MIT License (MIT) -Copyright (c) 2014-2015 Jon Schlinkert. - -Permission is hereby granted, free of charge, to any person -obtaining a copy of this software and associated documentation -files (the "Software"), to deal in the Software without -restriction, including without limitation the rights to use, -copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the -Software is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -OTHER DEALINGS IN THE SOFTWARE. - ------ - -The following software may be included in this product: arr-diff, clone-deep, fill-range, for-in, has-value, has-values, kind-of, normalize-path, set-value. A copy of the source code may be downloaded from https://github.com/jonschlinkert/arr-diff.git (arr-diff), https://github.com/jonschlinkert/clone-deep.git (clone-deep), https://github.com/jonschlinkert/fill-range.git (fill-range), https://github.com/jonschlinkert/for-in.git (for-in), https://github.com/jonschlinkert/has-value.git (has-value), https://github.com/jonschlinkert/has-values.git (has-values), https://github.com/jonschlinkert/kind-of.git (kind-of), https://github.com/jonschlinkert/normalize-path.git (normalize-path), https://github.com/jonschlinkert/set-value.git (set-value). This software contains the following license and notice below: - -The MIT License (MIT) - -Copyright (c) 2014-2017, Jon Schlinkert +Copyright (c) 2014-2015, Jon Schlinkert. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -5319,11 +5302,11 @@ THE SOFTWARE. ----- -The following software may be included in this product: arr-diff, arr-union, array-unique, extend-shallow, get-value, is-extglob, is-glob, is-number, is-primitive, isobject, longest, micromatch, mixin-object, object.omit, parse-filepath, relative, set-value, write. A copy of the source code may be downloaded from https://github.com/jonschlinkert/arr-diff.git (arr-diff), git://github.com/jonschlinkert/arr-union.git (arr-union), git://github.com/jonschlinkert/array-unique.git (array-unique), https://github.com/jonschlinkert/extend-shallow.git (extend-shallow), https://github.com/jonschlinkert/get-value.git (get-value), https://github.com/jonschlinkert/is-extglob.git (is-extglob), https://github.com/jonschlinkert/is-glob.git (is-glob), https://github.com/jonschlinkert/is-number.git (is-number), git://github.com/jonschlinkert/is-primitive.git (is-primitive), git://github.com/jonschlinkert/isobject.git (isobject), https://github.com/jonschlinkert/longest.git (longest), https://github.com/jonschlinkert/micromatch.git (micromatch), https://github.com/jonschlinkert/mixin-object.git (mixin-object), git://github.com/jonschlinkert/object.omit.git (object.omit), https://github.com/jonschlinkert/parse-filepath.git (parse-filepath), https://github.com/jonschlinkert/relative.git (relative), git://github.com/jonschlinkert/set-value.git (set-value), https://github.com/jonschlinkert/write.git (write). This software contains the following license and notice below: +The following software may be included in this product: arr-diff, clone-deep, fill-range, for-in, has-value, has-values, kind-of, normalize-path, set-value. A copy of the source code may be downloaded from https://github.com/jonschlinkert/arr-diff.git (arr-diff), https://github.com/jonschlinkert/clone-deep.git (clone-deep), https://github.com/jonschlinkert/fill-range.git (fill-range), https://github.com/jonschlinkert/for-in.git (for-in), https://github.com/jonschlinkert/has-value.git (has-value), https://github.com/jonschlinkert/has-values.git (has-values), https://github.com/jonschlinkert/kind-of.git (kind-of), https://github.com/jonschlinkert/normalize-path.git (normalize-path), https://github.com/jonschlinkert/set-value.git (set-value). This software contains the following license and notice below: The MIT License (MIT) -Copyright (c) 2014-2015, Jon Schlinkert. +Copyright (c) 2014-2017, Jon Schlinkert Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -5345,6 +5328,35 @@ THE SOFTWARE. ----- +The following software may be included in this product: arr-diff. A copy of the source code may be downloaded from https://github.com/jonschlinkert/arr-diff.git. This software contains the following license and notice below: + +The MIT License (MIT) + +Copyright (c) 2014-2015 Jon Schlinkert. + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +----- + The following software may be included in this product: arr-filter, filename-regex, for-own, object.defaults, object.reduce. A copy of the source code may be downloaded from https://github.com/jonschlinkert/arr-filter.git (arr-filter), https://github.com/regexhq/filename-regex.git (filename-regex), https://github.com/jonschlinkert/for-own.git (for-own), https://github.com/jonschlinkert/object.defaults.git (object.defaults), https://github.com/jonschlinkert/object.reduce.git (object.reduce). This software contains the following license and notice below: The MIT License (MIT) @@ -7776,7 +7788,7 @@ The following software may be included in this product: bl. A copy of the source The MIT License (MIT) ===================== -Copyright (c) 2013-2016 bl contributors +Copyright (c) 2013-2018 bl contributors ---------------------------------- *bl contributors listed at * @@ -7812,7 +7824,7 @@ The following software may be included in this product: bl. A copy of the source The MIT License (MIT) ===================== -Copyright (c) 2013-2018 bl contributors +Copyright (c) 2013-2016 bl contributors ---------------------------------- *bl contributors listed at * @@ -7829,7 +7841,7 @@ The following software may be included in this product: bluebird. A copy of the The MIT License (MIT) -Copyright (c) 2013-2018 Petka Antonov +Copyright (c) 2013-2015 Petka Antonov Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -7855,7 +7867,7 @@ The following software may be included in this product: bluebird. A copy of the The MIT License (MIT) -Copyright (c) 2013-2015 Petka Antonov +Copyright (c) 2013-2018 Petka Antonov Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -11877,7 +11889,7 @@ SOFTWARE. ----- -The following software may be included in this product: css-loader, enhanced-resolve, file-loader, loader-utils, mini-css-extract-plugin, schema-utils, terser-webpack-plugin, uglifyjs-webpack-plugin, url-loader, watchpack, webpack, webpack-dev-middleware, webpack-hot-middleware. A copy of the source code may be downloaded from https://github.com/webpack-contrib/css-loader.git (css-loader), git://github.com/webpack/enhanced-resolve.git (enhanced-resolve), https://github.com/webpack-contrib/file-loader.git (file-loader), https://github.com/webpack/loader-utils.git (loader-utils), https://github.com/webpack-contrib/mini-css-extract-plugin.git (mini-css-extract-plugin), https://github.com/webpack-contrib/schema-utils (schema-utils), https://github.com/webpack-contrib/terser-webpack-plugin.git (terser-webpack-plugin), https://github.com/webpack-contrib/uglifyjs-webpack-plugin.git (uglifyjs-webpack-plugin), https://github.com/webpack-contrib/url-loader.git (url-loader), https://github.com/webpack/watchpack.git (watchpack), https://github.com/webpack/webpack.git (webpack), https://github.com/webpack/webpack-dev-middleware.git (webpack-dev-middleware), https://github.com/webpack-contrib/webpack-hot-middleware.git (webpack-hot-middleware). This software contains the following license and notice below: +The following software may be included in this product: css-loader, enhanced-resolve, file-loader, loader-utils, memory-fs, mini-css-extract-plugin, schema-utils, terser-webpack-plugin, uglifyjs-webpack-plugin, url-loader, watchpack, webpack, webpack-dev-middleware, webpack-hot-middleware. A copy of the source code may be downloaded from https://github.com/webpack-contrib/css-loader.git (css-loader), git://github.com/webpack/enhanced-resolve.git (enhanced-resolve), https://github.com/webpack-contrib/file-loader.git (file-loader), https://github.com/webpack/loader-utils.git (loader-utils), https://github.com/webpack/memory-fs.git (memory-fs), https://github.com/webpack-contrib/mini-css-extract-plugin.git (mini-css-extract-plugin), https://github.com/webpack/schema-utils.git (schema-utils), https://github.com/webpack-contrib/terser-webpack-plugin.git (terser-webpack-plugin), https://github.com/webpack-contrib/uglifyjs-webpack-plugin.git (uglifyjs-webpack-plugin), https://github.com/webpack-contrib/url-loader.git (url-loader), https://github.com/webpack/watchpack.git (watchpack), https://github.com/webpack/webpack.git (webpack), https://github.com/webpack/webpack-dev-middleware.git (webpack-dev-middleware), https://github.com/webpack-contrib/webpack-hot-middleware.git (webpack-hot-middleware). This software contains the following license and notice below: Copyright JS Foundation and other contributors @@ -11930,6 +11942,30 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI ----- +The following software may be included in this product: css-tree. A copy of the source code may be downloaded from https://github.com/csstree/csstree.git. This software contains the following license and notice below: + +Copyright (C) 2016-2019 by Roman Dvornov + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +----- + The following software may be included in this product: cssom. A copy of the source code may be downloaded from https://github.com/NV/CSSOM.git. This software contains the following license and notice below: Copyright (c) Nikita Vasilyev @@ -12788,7 +12824,7 @@ The following software may be included in this product: detect-file. A copy of t The MIT License (MIT) -Copyright (c) 2016-2017, Brian Woodward. +Copyright (c) 2016, . Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -12814,7 +12850,7 @@ The following software may be included in this product: detect-file. A copy of t The MIT License (MIT) -Copyright (c) 2016, . +Copyright (c) 2016-2017, Brian Woodward. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -13070,6 +13106,33 @@ SOFTWARE. The following software may be included in this product: doctrine. A copy of the source code may be downloaded from https://github.com/eslint/doctrine.git. This software contains the following license and notice below: +Doctrine +Copyright jQuery Foundation and other contributors, https://jquery.org/ + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +----- + +The following software may be included in this product: doctrine. A copy of the source code may be downloaded from https://github.com/eslint/doctrine.git. This software contains the following license and notice below: + Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ @@ -13249,34 +13312,7 @@ END OF TERMS AND CONDITIONS ----- -The following software may be included in this product: doctrine, escodegen, estraverse, esutils, regjsparser. A copy of the source code may be downloaded from http://github.com/eslint/doctrine.git (doctrine), http://github.com/estools/escodegen.git (escodegen), http://github.com/estools/estraverse.git (estraverse), http://github.com/estools/esutils.git (esutils), git@github.com:jviereck/regjsparser.git (regjsparser). This software contains the following license and notice below: - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY -DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ------ - -The following software may be included in this product: doctrine. A copy of the source code may be downloaded from https://github.com/eslint/doctrine.git. This software contains the following license and notice below: - -Doctrine -Copyright jQuery Foundation and other contributors, https://jquery.org/ +The following software may be included in this product: doctrine, escodegen, estraverse, esutils, regjsparser. A copy of the source code may be downloaded from http://github.com/eslint/doctrine.git (doctrine), http://github.com/estools/escodegen.git (escodegen), http://github.com/estools/estraverse.git (estraverse), http://github.com/Constellation/esutils.git (esutils), git@github.com:jviereck/regjsparser.git (regjsparser). This software contains the following license and notice below: Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -13992,7 +14028,7 @@ SOFTWARE. ----- -The following software may be included in this product: enzyme, enzyme-adapter-react-16, enzyme-adapter-utils. A copy of the source code may be downloaded from https://github.com/airbnb/enzyme.git (enzyme), https://github.com/airbnb/enzyme.git (enzyme-adapter-react-16), https://github.com/airbnb/enzyme.git (enzyme-adapter-utils). This software contains the following license and notice below: +The following software may be included in this product: enzyme, enzyme-adapter-react-16, enzyme-adapter-utils, enzyme-shallow-equal. A copy of the source code may be downloaded from https://github.com/airbnb/enzyme.git (enzyme), https://github.com/airbnb/enzyme.git (enzyme-adapter-react-16), https://github.com/airbnb/enzyme.git (enzyme-adapter-utils), https://github.com/airbnb/enzyme.git (enzyme-shallow-equal). This software contains the following license and notice below: The MIT License (MIT) @@ -14498,7 +14534,6 @@ SOFTWARE. The following software may be included in this product: eslint-scope. A copy of the source code may be downloaded from https://github.com/eslint/eslint-scope.git. This software contains the following license and notice below: -eslint-scope Copyright JS Foundation and other contributors, https://js.foundation Copyright (C) 2012-2013 Yusuke Suzuki (twitter: @Constellation) and other contributors. @@ -14526,6 +14561,7 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. The following software may be included in this product: eslint-scope. A copy of the source code may be downloaded from https://github.com/eslint/eslint-scope.git. This software contains the following license and notice below: +eslint-scope Copyright JS Foundation and other contributors, https://js.foundation Copyright (C) 2012-2013 Yusuke Suzuki (twitter: @Constellation) and other contributors. @@ -14811,7 +14847,7 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. The following software may be included in this product: esprima. A copy of the source code may be downloaded from https://github.com/jquery/esprima.git. This software contains the following license and notice below: -Copyright (c) jQuery Foundation, Inc. and Contributors, All Rights Reserved. +Copyright JS Foundation and other contributors, https://js.foundation/ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -14837,7 +14873,7 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. The following software may be included in this product: esprima. A copy of the source code may be downloaded from https://github.com/jquery/esprima.git. This software contains the following license and notice below: -Copyright JS Foundation and other contributors, https://js.foundation/ +Copyright (c) jQuery Foundation, Inc. and Contributors, All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -15398,6 +15434,32 @@ Exhibit B - "Incompatible With Secondary Licenses" Notice ----- +The following software may be included in this product: ethereum-bloom-filters. A copy of the source code may be downloaded from git+https://github.com/joshstevens19/ethereum-bloom-filters.git. This software contains the following license and notice below: + +MIT License + +Copyright (c) 2019 Josh Stevens + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +----- + The following software may be included in this product: ethereum-common, ethereumjs-common. A copy of the source code may be downloaded from git+https://github.com/ethereumjs/common.git (ethereum-common), git+https://github.com/ethereumjs/ethereumjs-common.git (ethereumjs-common). This software contains the following license and notice below: The MIT License (MIT) @@ -19392,7 +19454,7 @@ The bundled Google Closure Library is licensed under Apache 2.0: ----- -The following software may be included in this product: google-p12-pem. A copy of the source code may be downloaded from https://github.com/google/google-p12-pem. This software contains the following license and notice below: +The following software may be included in this product: google-p12-pem. This software contains the following license and notice below: The MIT License (MIT) @@ -21738,6 +21800,31 @@ THE SOFTWARE. ----- +The following software may be included in this product: is2. A copy of the source code may be downloaded from git@github.com:stdarg/is2.git. This software contains the following license and notice below: + +The MIT License (MIT) + +Copyright (c) 2013 Edmond Meinfelder + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +----- + The following software may be included in this product: isomorphic-fetch. A copy of the source code may be downloaded from https://github.com/matthew-andrews/isomorphic-fetch.git. This software contains the following license and notice below: The MIT License (MIT) @@ -22120,7 +22207,7 @@ SOFTWARE. The following software may be included in this product: js-sha3. A copy of the source code may be downloaded from https://github.com/emn178/js-sha3.git. This software contains the following license and notice below: -Copyright 2015-2016 Chen, Yi-Cyuan +Copyright 2015-2017 Chen, Yi-Cyuan Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the @@ -22145,7 +22232,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. The following software may be included in this product: js-sha3. A copy of the source code may be downloaded from https://github.com/emn178/js-sha3.git. This software contains the following license and notice below: -Copyright 2015-2017 Chen, Yi-Cyuan +Copyright 2015-2018 Chen, Yi-Cyuan Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the @@ -22170,7 +22257,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. The following software may be included in this product: js-sha3. A copy of the source code may be downloaded from https://github.com/emn178/js-sha3.git. This software contains the following license and notice below: -Copyright 2015-2018 Chen, Yi-Cyuan +Copyright 2015-2016 Chen, Yi-Cyuan Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the @@ -22219,11 +22306,11 @@ THE SOFTWARE. ----- -The following software may be included in this product: js-tokens. A copy of the source code may be downloaded from https://github.com/lydell/js-tokens.git. This software contains the following license and notice below: +The following software may be included in this product: js-tokens, source-map-resolve. A copy of the source code may be downloaded from https://github.com/lydell/js-tokens.git (js-tokens), https://github.com/lydell/source-map-resolve.git (source-map-resolve). This software contains the following license and notice below: The MIT License (MIT) -Copyright (c) 2014, 2015, 2016, 2017, 2018 Simon Lydell +Copyright (c) 2014, 2015, 2016, 2017 Simon Lydell Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -22245,11 +22332,11 @@ THE SOFTWARE. ----- -The following software may be included in this product: js-tokens, source-map-resolve. A copy of the source code may be downloaded from https://github.com/lydell/js-tokens.git (js-tokens), https://github.com/lydell/source-map-resolve.git (source-map-resolve). This software contains the following license and notice below: +The following software may be included in this product: js-tokens. A copy of the source code may be downloaded from https://github.com/lydell/js-tokens.git. This software contains the following license and notice below: The MIT License (MIT) -Copyright (c) 2014, 2015, 2016, 2017 Simon Lydell +Copyright (c) 2014, 2015, 2016, 2017, 2018 Simon Lydell Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -22901,7 +22988,7 @@ SOFTWARE. ----- -The following software may be included in this product: keccakjs, scrypt.js. A copy of the source code may be downloaded from https://github.com/axic/keccakjs (keccakjs), https://github.com/axic/scrypt.js (scrypt.js). This software contains the following license and notice below: +The following software may be included in this product: keccakjs, scrypt.js. A copy of the source code may be downloaded from https://github.com/axic/keccakjs (keccakjs), https://github.com/axic/scryptjs (scrypt.js). This software contains the following license and notice below: The MIT License (MIT) @@ -23026,24 +23113,6 @@ THE SOFTWARE. ----- -The following software may be included in this product: level-codec, level-iterator-stream. A copy of the source code may be downloaded from https://github.com/Level/codec.git (level-codec), https://github.com/Level/iterator-stream.git (level-iterator-stream). This software contains the following license and notice below: - -The MIT License (MIT) -===================== - -Copyright (c) 2012-2015 LevelUP contributors ---------------------------------------- - -*LevelUP contributors listed at * - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - ------ - The following software may be included in this product: level-codec, level-errors. A copy of the source code may be downloaded from https://github.com/Level/codec.git (level-codec), https://github.com/Level/errors.git (level-errors). This software contains the following license and notice below: # The MIT License (MIT) @@ -23070,6 +23139,24 @@ SOFTWARE. ----- +The following software may be included in this product: level-codec, level-iterator-stream. A copy of the source code may be downloaded from https://github.com/Level/codec.git (level-codec), https://github.com/Level/iterator-stream.git (level-iterator-stream). This software contains the following license and notice below: + +The MIT License (MIT) +===================== + +Copyright (c) 2012-2015 LevelUP contributors +--------------------------------------- + +*LevelUP contributors listed at * + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +----- + The following software may be included in this product: level-errors. A copy of the source code may be downloaded from https://github.com/level/errors.git. This software contains the following license and notice below: The MIT License (MIT) @@ -23204,9 +23291,13 @@ Original Author, when distributed with the Software. The following software may be included in this product: levelup. A copy of the source code may be downloaded from https://github.com/level/levelup.git. This software contains the following license and notice below: -# The MIT License (MIT) +The MIT License (MIT) +===================== -**Copyright © 2012-present `levelup` [Contributors](CONTRIBUTORS.md).** +Copyright (c) 2012-2016 LevelUP contributors +--------------------------------------- + +*LevelUP contributors listed at * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: @@ -23218,13 +23309,9 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI The following software may be included in this product: levelup. A copy of the source code may be downloaded from https://github.com/level/levelup.git. This software contains the following license and notice below: -The MIT License (MIT) -===================== - -Copyright (c) 2012-2016 LevelUP contributors ---------------------------------------- +# The MIT License (MIT) -*LevelUP contributors listed at * +**Copyright © 2012-present `levelup` [Contributors](CONTRIBUTORS.md).** Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: @@ -24268,29 +24355,124 @@ SOFTWARE. ----- -The following software may be included in this product: memdown. A copy of the source code may be downloaded from https://github.com/Level/memdown.git. This software contains the following license and notice below: +The following software may be included in this product: mdn-data. A copy of the source code may be downloaded from https://github.com/mdn/data.git. This software contains the following license and notice below: -The MIT License (MIT) +CC0 1.0 Universal -Copyright (c) 2013-2018 Rod Vagg (the "Original Author") +Statement of Purpose -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator and +subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. +Certain owners wish to permanently relinquish those rights to a Work for the +purpose of contributing to a commons of creative, cultural and scientific +works ("Commons") that the public can reliably and without fear of later +claims of infringement build upon, modify, incorporate in other works, reuse +and redistribute as freely as possible in any form whatsoever and for any +purposes, including without limitation commercial purposes. These owners may +contribute to the Commons to promote the ideal of a free culture and the +further production of creative, cultural and scientific works, or to gain +reputation or greater distribution for their Work in part through the use and +efforts of others. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +For these and/or other purposes and motivations, and without any expectation +of additional consideration or compensation, the person associating CC0 with a +Work (the "Affirmer"), to the extent that he or she is an owner of Copyright +and Related Rights in the Work, voluntarily elects to apply CC0 to the Work +and publicly distribute the Work under its terms, with knowledge of his or her +Copyright and Related Rights in the Work and the meaning and intended legal +effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not limited +to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, communicate, + and translate a Work; + + ii. moral rights retained by the original author(s) and/or performer(s); + + iii. publicity and privacy rights pertaining to a person's image or likeness + depicted in a Work; + + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + + v. rights protecting the extraction, dissemination, use and reuse of data in + a Work; + + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation thereof, + including any amended or successor version of such directive); and + + vii. other similar, equivalent or corresponding rights throughout the world + based on applicable law or treaty, and any national implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention of, +applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and +unconditionally waives, abandons, and surrenders all of Affirmer's Copyright +and Related Rights and associated claims and causes of action, whether now +known or unknown (including existing as well as future claims and causes of +action), in the Work (i) in all territories worldwide, (ii) for the maximum +duration provided by applicable law or treaty (including future time +extensions), (iii) in any current or future medium and for any number of +copies, and (iv) for any purpose whatsoever, including without limitation +commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes +the Waiver for the benefit of each member of the public at large and to the +detriment of Affirmer's heirs and successors, fully intending that such Waiver +shall not be subject to revocation, rescission, cancellation, termination, or +any other legal or equitable action to disrupt the quiet enjoyment of the Work +by the public as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason be +judged legally invalid or ineffective under applicable law, then the Waiver +shall be preserved to the maximum extent permitted taking into account +Affirmer's express Statement of Purpose. In addition, to the extent the Waiver +is so judged Affirmer hereby grants to each affected person a royalty-free, +non transferable, non sublicensable, non exclusive, irrevocable and +unconditional license to exercise Affirmer's Copyright and Related Rights in +the Work (i) in all territories worldwide, (ii) for the maximum duration +provided by applicable law or treaty (including future time extensions), (iii) +in any current or future medium and for any number of copies, and (iv) for any +purpose whatsoever, including without limitation commercial, advertising or +promotional purposes (the "License"). The License shall be deemed effective as +of the date CC0 was applied by Affirmer to the Work. Should any part of the +License for any reason be judged legally invalid or ineffective under +applicable law, such partial invalidity or ineffectiveness shall not +invalidate the remainder of the License, and in such case Affirmer hereby +affirms that he or she will not (i) exercise any of his or her remaining +Copyright and Related Rights in the Work or (ii) assert any associated claims +and causes of action with respect to the Work, in either case contrary to +Affirmer's express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + + b. Affirmer offers the Work as-is and makes no representations or warranties + of any kind concerning the Work, express, implied, statutory or otherwise, + including without limitation warranties of title, merchantability, fitness + for a particular purpose, non infringement, or the absence of latent or + other defects, accuracy, or the present or absence of errors, whether or not + discoverable, all to the greatest extent permissible under applicable law. + + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without limitation + any person's Copyright and Related Rights in the Work. Further, Affirmer + disclaims responsibility for obtaining any necessary consents, permissions + or other rights required for any use of the Work. + + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to this + CC0 or use of the Work. + +For more information, please see + ----- @@ -24338,6 +24520,32 @@ Original Author, when distributed with the Software. ----- +The following software may be included in this product: memdown. A copy of the source code may be downloaded from https://github.com/Level/memdown.git. This software contains the following license and notice below: + +The MIT License (MIT) + +Copyright (c) 2013-2018 Rod Vagg (the "Original Author") + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +----- + The following software may be included in this product: memoize-one. A copy of the source code may be downloaded from https://github.com/alexreardon/memoize-one.git. This software contains the following license and notice below: MIT License @@ -28182,9 +28390,9 @@ SOFTWARE. ----- -The following software may be included in this product: performance-now. A copy of the source code may be downloaded from git://github.com/meryn/performance-now.git. This software contains the following license and notice below: +The following software may be included in this product: performance-now. A copy of the source code may be downloaded from git://github.com/braveg1rl/performance-now.git. This software contains the following license and notice below: -Copyright (c) 2013 Meryn Stol +Copyright (c) 2013 Braveg1rl Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: @@ -28194,9 +28402,9 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI ----- -The following software may be included in this product: performance-now. A copy of the source code may be downloaded from git://github.com/braveg1rl/performance-now.git. This software contains the following license and notice below: +The following software may be included in this product: performance-now. A copy of the source code may be downloaded from git://github.com/meryn/performance-now.git. This software contains the following license and notice below: -Copyright (c) 2013 Braveg1rl +Copyright (c) 2013 Meryn Stol Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: @@ -28856,11 +29064,11 @@ SOFTWARE.** ----- -The following software may be included in this product: progress, supertest. A copy of the source code may be downloaded from git://github.com/visionmedia/node-progress (progress), https://github.com/visionmedia/supertest.git (supertest). This software contains the following license and notice below: +The following software may be included in this product: progress. A copy of the source code may be downloaded from git://github.com/visionmedia/node-progress. This software contains the following license and notice below: (The MIT License) -Copyright (c) 2014 TJ Holowaychuk +Copyright (c) 2017 TJ Holowaychuk Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the @@ -28883,11 +29091,11 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ----- -The following software may be included in this product: progress. A copy of the source code may be downloaded from git://github.com/visionmedia/node-progress. This software contains the following license and notice below: +The following software may be included in this product: progress, supertest. A copy of the source code may be downloaded from git://github.com/visionmedia/node-progress (progress), https://github.com/visionmedia/supertest.git (supertest). This software contains the following license and notice below: (The MIT License) -Copyright (c) 2017 TJ Holowaychuk +Copyright (c) 2014 TJ Holowaychuk Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the @@ -29452,7 +29660,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. The following software may be included in this product: q. A copy of the source code may be downloaded from git://github.com/kriskowal/q.git. This software contains the following license and notice below: -Copyright 2009–2017 Kristopher Michael Kowal. All rights reserved. +Copyright 2009–2014 Kristopher Michael Kowal. All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the @@ -29475,7 +29683,7 @@ IN THE SOFTWARE. The following software may be included in this product: q. A copy of the source code may be downloaded from git://github.com/kriskowal/q.git. This software contains the following license and notice below: -Copyright 2009–2014 Kristopher Michael Kowal. All rights reserved. +Copyright 2009–2017 Kristopher Michael Kowal. All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the @@ -30163,7 +30371,7 @@ SOFTWARE. ----- -The following software may be included in this product: react-native-autocomplete-input. A copy of the source code may be downloaded from git+ssh://git@github.com/l-urence/react-native-autocomplete-input.git. This software contains the following license and notice below: +The following software may be included in this product: react-native-autocomplete-input. A copy of the source code may be downloaded from git+ssh://git@github.com/mrlaessig/react-native-autocomplete-input.git. This software contains the following license and notice below: The MIT License (MIT) @@ -30357,6 +30565,32 @@ SOFTWARE. ----- +The following software may be included in this product: react-native-exit-app. A copy of the source code may be downloaded from https://github.com/wumke/react-native-exit-app. This software contains the following license and notice below: + +MIT License + +Copyright (c) 2018 Wumke + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +----- + The following software may be included in this product: react-native-firebase. A copy of the source code may be downloaded from https://github.com/invertase/react-native-firebase.git. This software contains the following license and notice below: Copyright (c) 2018 Invertase Limited @@ -30711,6 +30945,32 @@ SOFTWARE. ----- +The following software may be included in this product: react-native-safe-area-context. A copy of the source code may be downloaded from https://github.com/th3rdwave/react-native-safe-area-context.git. This software contains the following license and notice below: + +MIT License + +Copyright (c) 2019 Th3rd Wave + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +----- + The following software may be included in this product: react-native-safe-area-view. A copy of the source code may be downloaded from git@github.com:react-community/react-native-safe-area-view.git. This software contains the following license and notice below: MIT License @@ -31117,7 +31377,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ----- -The following software may be included in this product: react-redux, redux, redux-thunk. A copy of the source code may be downloaded from https://github.com/reduxjs/react-redux.git (react-redux), https://github.com/reduxjs/redux.git (redux), https://github.com/reduxjs/redux-thunk.git (redux-thunk). This software contains the following license and notice below: +The following software may be included in this product: react-redux, redux. A copy of the source code may be downloaded from https://github.com/reduxjs/react-redux.git (react-redux), https://github.com/reduxjs/redux.git (redux). This software contains the following license and notice below: The MIT License (MIT) @@ -31356,6 +31616,31 @@ IN THE SOFTWARE. The following software may be included in this product: readdirp. A copy of the source code may be downloaded from git://github.com/paulmillr/readdirp.git. This software contains the following license and notice below: +This software is released under the MIT license: + +Copyright (c) 2012-2015 Thorsten Lorenz + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +----- + +The following software may be included in this product: readdirp. A copy of the source code may be downloaded from git://github.com/paulmillr/readdirp.git. This software contains the following license and notice below: + MIT License Copyright (c) 2012-2019 Thorsten Lorenz, Paul Miller (https://paulmillr.com) @@ -31380,31 +31665,6 @@ SOFTWARE. ----- -The following software may be included in this product: readdirp. A copy of the source code may be downloaded from git://github.com/paulmillr/readdirp.git. This software contains the following license and notice below: - -This software is released under the MIT license: - -Copyright (c) 2012-2015 Thorsten Lorenz - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - ------ - The following software may be included in this product: realpath-native. A copy of the source code may be downloaded from https://github.com/SimenB/realpath-native.git. This software contains the following license and notice below: MIT License @@ -31719,9 +31979,11 @@ SOFTWARE. ----- -The following software may be included in this product: regjsgen. A copy of the source code may be downloaded from https://github.com/d10/regjsgen.git. This software contains the following license and notice below: +The following software may be included in this product: regjsgen. A copy of the source code may be downloaded from https://github.com/bnjmnt4n/regjsgen.git. This software contains the following license and notice below: -Copyright 2014 Benjamin Tan (https://d10.github.io/) +The MIT License (MIT) + +Copyright 2014-2019 Benjamin Tan Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the @@ -31744,11 +32006,9 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ----- -The following software may be included in this product: regjsgen. A copy of the source code may be downloaded from https://github.com/bnjmnt4n/regjsgen.git. This software contains the following license and notice below: - -The MIT License (MIT) +The following software may be included in this product: regjsgen. A copy of the source code may be downloaded from https://github.com/d10/regjsgen.git. This software contains the following license and notice below: -Copyright 2014-2018 Benjamin Tan +Copyright 2014 Benjamin Tan (https://d10.github.io/) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the @@ -32400,7 +32660,7 @@ SOFTWARE. ----- -The following software may be included in this product: rxjs. A copy of the source code may be downloaded from https://github.com/reactivex/rxjs.git. This software contains the following license and notice below: +The following software may be included in this product: rxjs. A copy of the source code may be downloaded from git@github.com:ReactiveX/RxJS.git. This software contains the following license and notice below: Apache License Version 2.0, January 2004 @@ -32590,7 +32850,7 @@ Apache License same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright (c) 2015-2018 Google, Inc., Netflix, Inc., Microsoft Corp. and contributors + Copyright (c) 2015-2017 Google, Inc., Netflix, Inc., Microsoft Corp. and contributors Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -32606,7 +32866,7 @@ Apache License ----- -The following software may be included in this product: rxjs. A copy of the source code may be downloaded from git@github.com:ReactiveX/RxJS.git. This software contains the following license and notice below: +The following software may be included in this product: rxjs. A copy of the source code may be downloaded from https://github.com/reactivex/rxjs.git. This software contains the following license and notice below: Apache License Version 2.0, January 2004 @@ -32796,7 +33056,7 @@ Apache License same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright (c) 2015-2017 Google, Inc., Netflix, Inc., Microsoft Corp. and contributors + Copyright (c) 2015-2018 Google, Inc., Netflix, Inc., Microsoft Corp. and contributors Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -34061,6 +34321,20 @@ THE SOFTWARE. The following software may be included in this product: slice-ansi. A copy of the source code may be downloaded from https://github.com/chalk/slice-ansi.git. This software contains the following license and notice below: +MIT License + +Copyright (c) DC + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +----- + +The following software may be included in this product: slice-ansi. A copy of the source code may be downloaded from https://github.com/chalk/slice-ansi.git. This software contains the following license and notice below: + (The MIT License) Copyright (c) 2015 DC @@ -34086,20 +34360,6 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ----- -The following software may be included in this product: slice-ansi. A copy of the source code may be downloaded from https://github.com/chalk/slice-ansi.git. This software contains the following license and notice below: - -MIT License - -Copyright (c) DC - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - ------ - The following software may be included in this product: sliced. A copy of the source code may be downloaded from git://github.com/aheckmann/sliced. This software contains the following license and notice below: (The MIT License) @@ -35400,6 +35660,31 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ----- +The following software may be included in this product: tcp-port-used. A copy of the source code may be downloaded from git://github.com/stdarg/tcp-port-used.git. This software contains the following license and notice below: + +The MIT License (MIT) + +Copyright (c) 2013 jut-io + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +----- + The following software may be included in this product: tdigest. A copy of the source code may be downloaded from https://github.com/welch/tdigest.git. This software contains the following license and notice below: The MIT License (MIT) @@ -38590,7 +38875,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ----- -The following software may be included in this product: uuid. A copy of the source code may be downloaded from https://github.com/shtylman/node-uuid.git. This software contains the following license and notice below: +The following software may be included in this product: uuid. A copy of the source code may be downloaded from https://github.com/defunctzombie/node-uuid.git. This software contains the following license and notice below: Copyright (c) 2010-2012 Robert Kieffer MIT License - http://opensource.org/licenses/mit-license.php @@ -39698,7 +39983,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI ----- -The following software may be included in this product: workspace-aggregator-f7085830-6625-41b2-ba95-4a0cd827c7a2. This software contains the following license and notice below: +The following software may be included in this product: workspace-aggregator-b8423bb7-31c8-4bc0-83fe-ac5ca53b154a. This software contains the following license and notice below: Apache License Version 2.0, January 2004 diff --git a/packages/mobile/android/app/src/main/java/org/celo/mobile/MainActivity.java b/packages/mobile/android/app/src/main/java/org/celo/mobile/MainActivity.java index caae9f98e55..fee31cb894b 100644 --- a/packages/mobile/android/app/src/main/java/org/celo/mobile/MainActivity.java +++ b/packages/mobile/android/app/src/main/java/org/celo/mobile/MainActivity.java @@ -6,6 +6,9 @@ import com.facebook.react.ReactInstanceManager; import com.facebook.react.bridge.ReactContext; import com.facebook.react.modules.core.DeviceEventManagerModule; +import com.facebook.react.ReactActivityDelegate; +import com.facebook.react.ReactRootView; +import com.swmansion.gesturehandler.react.RNGestureHandlerEnabledRootView; import org.devio.rn.splashscreen.SplashScreen; @@ -45,6 +48,17 @@ public void onPause() { @Override public void onReactContextInitialized(ReactContext context) { - context.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit("AppStartedLoading", appStartTimestamp.toString()); + context.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit("AppStartedLoading", + appStartTimestamp.toString()); + } + + @Override + protected ReactActivityDelegate createReactActivityDelegate() { + return new ReactActivityDelegate(this, getMainComponentName()) { + @Override + protected ReactRootView createRootView() { + return new RNGestureHandlerEnabledRootView(MainActivity.this); + } + }; } } diff --git a/packages/mobile/android/app/src/main/java/org/celo/mobile/MainApplication.java b/packages/mobile/android/app/src/main/java/org/celo/mobile/MainApplication.java index 75fa6f6031e..f69f52d3af3 100644 --- a/packages/mobile/android/app/src/main/java/org/celo/mobile/MainApplication.java +++ b/packages/mobile/android/app/src/main/java/org/celo/mobile/MainApplication.java @@ -3,57 +3,20 @@ import android.content.Context; import android.app.Application; -import org.reactnative.camera.RNCameraPackage; -import com.chirag.RNMail.RNMail; -import org.devio.rn.splashscreen.SplashScreenReactPackage; -import io.xogus.reactnative.versioncheck.RNVersionCheckPackage; -import com.levelasquez.androidopensettings.AndroidOpenSettingsPackage; -import com.tradle.react.UdpSocketsModule; - -import io.invertase.firebase.RNFirebasePackage; -import io.invertase.firebase.messaging.RNFirebaseMessagingPackage; -import io.invertase.firebase.notifications.RNFirebaseNotificationsPackage; -import io.invertase.firebase.storage.RNFirebaseStoragePackage; -import io.invertase.firebase.database.RNFirebaseDatabasePackage; -import io.invertase.firebase.auth.RNFirebaseAuthPackage; -import com.peel.react.TcpSocketsModule; -import io.sentry.RNSentryPackage; -import com.bitgo.randombytes.RandomBytesPackage; -import com.corbt.keepawake.KCKeepAwakePackage; +import com.facebook.react.PackageList; import com.facebook.react.ReactApplication; -import com.burnweb.rnsendintent.RNSendIntentPackage; -import com.reactnativecommunity.webview.RNCWebViewPackage; -import com.reactnativecommunity.netinfo.NetInfoPackage; -import com.swmansion.gesturehandler.react.RNGestureHandlerPackage; -import com.segment.analytics.reactnative.integration.firebase.RNAnalyticsIntegration_FirebasePackage; -import com.segment.analytics.reactnative.core.RNAnalyticsPackage; +import io.sentry.RNSentryPackage; +import com.reactnativegeth.RNGethPackage; import com.facebook.react.ReactNativeHost; import com.facebook.react.ReactPackage; import com.facebook.react.shell.MainReactPackage; import com.facebook.soloader.SoLoader; -import com.horcrux.svg.SvgPackage; -import com.kristiansorens.flagsecure.FlagSecurePackage; -import com.learnium.RNDeviceInfo.RNDeviceInfo; -import com.levelasquez.androidopensettings.AndroidOpenSettingsPackage; -import com.lugg.ReactNativeConfig.ReactNativeConfigPackage; -import com.peel.react.TcpSocketsModule; -import com.reactcommunity.rnlocalize.RNLocalizePackage; -import com.reactnativegeth.RNGethPackage; -import com.rnfs.RNFSPackage; -import com.rt2zz.reactnativecontacts.ReactNativeContacts; -import com.tradle.react.UdpSocketsModule; import org.celo.devicecredentials.RNConfirmDeviceCredentialsPackage; -import org.devio.rn.splashscreen.SplashScreenReactPackage; -import com.rnrestartandroid.RNRestartAndroidPackage; -import me.furtado.smsretriever.RNSmsRetrieverPackage; -import cl.json.RNSharePackage; import cl.json.ShareApplication; -import com.rninstallreferrer.RNInstallReferrerPackage; -import com.reactlibrary.securekeystore.RNSecureKeyStorePackage; -import com.th3rdwave.safeareacontext.SafeAreaContextPackage; import android.util.Log; +import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -62,8 +25,11 @@ import ru.ivanarh.jndcrash.NDCrash; import ru.ivanarh.jndcrash.NDCrashUnwinder; -// Disabled due to dex count -// import com.swmansion.rnscreens.RNScreensPackage; +import io.invertase.firebase.auth.RNFirebaseAuthPackage; +import io.invertase.firebase.database.RNFirebaseDatabasePackage; +import io.invertase.firebase.storage.RNFirebaseStoragePackage; +import io.invertase.firebase.messaging.RNFirebaseMessagingPackage; +import io.invertase.firebase.notifications.RNFirebaseNotificationsPackage; public class MainApplication extends Application implements ShareApplication, ReactApplication { @@ -75,58 +41,21 @@ public boolean getUseDeveloperSupport() { @Override protected List getPackages() { - - ReactPackage basePackages[] = new ReactPackage[] { - new MainReactPackage(), - new RNInstallReferrerPackage(), - new RNSendIntentPackage(), - new RNCWebViewPackage(), - new NetInfoPackage(), - new RNGestureHandlerPackage(), - new RNAnalyticsIntegration_FirebasePackage(), - new RNAnalyticsPackage(), - new RNCameraPackage(), - new RNMail(), - new SplashScreenReactPackage(), - new AndroidOpenSettingsPackage(), - new UdpSocketsModule(), - new RNLocalizePackage(), - new ReactNativeConfigPackage(), - new RNFirebasePackage(), - new RNFirebaseMessagingPackage(), - new RNFirebaseNotificationsPackage(), - new RNFirebaseDatabasePackage(), - new RNFirebaseAuthPackage(), - new TcpSocketsModule(), - new RNSentryPackage(), - new RandomBytesPackage(), - new SvgPackage(), - new ReactNativeContacts(), - new KCKeepAwakePackage(), - new RNDeviceInfo(), - new RNFSPackage(), - new RNGethPackage(), - new FlagSecurePackage(), - new RNFirebaseStoragePackage(), - new RNVersionCheckPackage(), - new RNRestartAndroidPackage(), - new RNSmsRetrieverPackage(), - new RNSharePackage(), - new RNSecureKeyStorePackage(), - new SafeAreaContextPackage() - // Disabled due to dex count - // new RNScreensPackage(), - }; - List packageList = new ArrayList<>(); - packageList.addAll(Arrays.asList(basePackages)); + @SuppressWarnings("UnnecessaryLocalVariable") + List packages = new PackageList(this).getPackages(); if (android.os.Build.VERSION.SDK_INT >= 23) { // Don't add this package below API 23, since it leads to // ClassDefNotFoundError due to classes which are only available // above API 23. - packageList.add(new RNConfirmDeviceCredentialsPackage()); + packages.add(new RNConfirmDeviceCredentialsPackage()); } - - return packageList; + packages.add(new RNGethPackage()); + packages.add(new RNFirebaseAuthPackage()); + packages.add(new RNFirebaseDatabasePackage()); + packages.add(new RNFirebaseStoragePackage()); + packages.add(new RNFirebaseMessagingPackage()); + packages.add(new RNFirebaseNotificationsPackage()); + return packages; } @Override @@ -144,6 +73,7 @@ public ReactNativeHost getReactNativeHost() { public void onCreate() { super.onCreate(); SoLoader.init(this, /* native exopackage */ false); + initializeFlipper(this); // Remove this line if you don't want Flipper enabled initNdkCrashHandler(); } @@ -163,4 +93,30 @@ private void initNdkCrashHandler() { Log.e("MainApplication@initJndcrash", "NDK crash handler init failed: " + error); } } + + /** + * Loads Flipper in React Native templates. + * + * @param context + */ + private static void initializeFlipper(Context context) { + if (BuildConfig.DEBUG) { + try { + /* + * We use reflection here to pick up the class that initializes Flipper, since + * Flipper library is not available in release mode + */ + Class aClass = Class.forName("com.facebook.flipper.ReactNativeFlipper"); + aClass.getMethod("initializeFlipper", Context.class).invoke(null, context); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } catch (NoSuchMethodException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } catch (InvocationTargetException e) { + e.printStackTrace(); + } + } + } } diff --git a/packages/mobile/android/app/src/main/res/values/styles.xml b/packages/mobile/android/app/src/main/res/values/styles.xml index daa8b4b6a35..fca3de0c954 100644 --- a/packages/mobile/android/app/src/main/res/values/styles.xml +++ b/packages/mobile/android/app/src/main/res/values/styles.xml @@ -1,6 +1,7 @@ diff --git a/packages/mobile/android/build.gradle b/packages/mobile/android/build.gradle index 3a5899da172..7844f5f6f2c 100644 --- a/packages/mobile/android/build.gradle +++ b/packages/mobile/android/build.gradle @@ -7,10 +7,7 @@ buildscript { compileSdkVersion = 28 targetSdkVersion = 28 supportLibVersion = "28.0.0" - kotlinVersion = '1.3.0' - detoxKotlinVersion = '1.3.0' - // Must pin to 16 because 17 uses androidX - googlePlayServicesVersion = "16.+" + kotlinVersion = "1.3.41" // Change this to change the geth version celoClientDirectory = new File(rootProject.projectDir, '../../../node_modules/@celo/client/build/bin') } @@ -19,8 +16,8 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.3.1' - classpath 'com.google.gms:google-services:4.2.0' + classpath 'com.android.tools.build:gradle:3.4.2' + classpath 'com.google.gms:google-services:4.3.2' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion" // NOTE: Do not place your application dependencies here; they belong @@ -31,11 +28,13 @@ buildscript { allprojects { repositories { mavenLocal() - google() - jcenter() maven { // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm - url "$rootDir/../node_modules/react-native/android" + url("$rootDir/../../../node_modules/react-native/android") + } + maven { + // Android JSC is installed from npm + url("$rootDir/../../../node_modules/jsc-android/dist") } maven { url "$rootDir/../../../node_modules/detox/Detox-android" @@ -43,30 +42,24 @@ allprojects { flatDir { dirs celoClientDirectory } - maven { - url "https://www.jitpack.io" - } - maven { - url "https://maven.google.com" - } + google() + jcenter() + maven { url 'https://jitpack.io' } } } subprojects { - project.configurations.all { - resolutionStrategy.eachDependency { details -> - if (details.requested.group == 'com.android.support' - && !details.requested.name.contains('multidex') ) { - details.useVersion rootProject.ext.supportLibVersion - } - if (details.requested.group == 'com.segment.analytics.android.integrations' - && details.requested.name == 'firebase' ) { - details.useVersion "1.2.0" - } + { project -> + if (project.name.contains('react-native-firebase')) { + buildscript { + dependencies { + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.41" } + } } + } - afterEvaluate {project -> + afterEvaluate {project -> if (project.hasProperty("android")) { android { compileSdkVersion rootProject.ext.compileSdkVersion diff --git a/packages/mobile/android/gradle.properties b/packages/mobile/android/gradle.properties index ed4ea35a9d8..fe07d833dce 100644 --- a/packages/mobile/android/gradle.properties +++ b/packages/mobile/android/gradle.properties @@ -19,5 +19,10 @@ CELO_RELEASE_STORE_FILE=celo-release-key.keystore CELO_RELEASE_KEY_ALIAS=celo-key-alias + # Setting this manually based on version number until we have this deploying via Cloud Build -VERSION_CODE=100500001 \ No newline at end of file +# Example: v1.5.1 deployment number 1 = 100500101 +VERSION_CODE=100500101 + +android.useAndroidX=true +android.enableJetifier=true diff --git a/packages/mobile/android/gradle/wrapper/gradle-wrapper.properties b/packages/mobile/android/gradle/wrapper/gradle-wrapper.properties index 019065d1d65..29e566ccd7d 100644 --- a/packages/mobile/android/gradle/wrapper/gradle-wrapper.properties +++ b/packages/mobile/android/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-5.5-all.zip zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip +zipStorePath=wrapper/dists \ No newline at end of file diff --git a/packages/mobile/android/settings.gradle b/packages/mobile/android/settings.gradle index f6b9eba5862..0ed524b325d 100644 --- a/packages/mobile/android/settings.gradle +++ b/packages/mobile/android/settings.gradle @@ -1,68 +1,7 @@ rootProject.name = 'celo' -include ':react-native-install-referrer' -project(':react-native-install-referrer').projectDir = new File(rootProject.projectDir, '../../../node_modules/react-native-install-referrer/android') -include ':react-native-send-intent' -project(':react-native-send-intent').projectDir = new File(rootProject.projectDir, '../../../node_modules/react-native-send-intent/android') -include ':react-native-webview' -project(':react-native-webview').projectDir = new File(rootProject.projectDir, '../../../node_modules/react-native-webview/android') -include ':@react-native-community_netinfo' -project(':@react-native-community_netinfo').projectDir = new File(rootProject.projectDir, '../../../node_modules/@react-native-community/netinfo/android') -include ':react-native-screens' -project(':react-native-screens').projectDir = new File(rootProject.projectDir, '../../../node_modules/react-native-screens/android') -include ':react-native-gesture-handler' -project(':react-native-gesture-handler').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-gesture-handler/android') -include ':@segment_analytics-react-native-firebase' -project(':@segment_analytics-react-native-firebase').projectDir = new File(rootProject.projectDir, '../../../node_modules/@segment/analytics-react-native-firebase/android') -include ':@segment_analytics-react-native' -project(':@segment_analytics-react-native').projectDir = new File(rootProject.projectDir, '../../../node_modules/@segment/analytics-react-native/android') -include ':react-native-camera' -project(':react-native-camera').projectDir = new File(rootProject.projectDir, '../../../node_modules/react-native-camera/android') -include ':react-native-mail' -project(':react-native-mail').projectDir = new File(rootProject.projectDir, '../../../node_modules/react-native-mail/android') -include ':react-native-splash-screen' -project(':react-native-splash-screen').projectDir = new File(rootProject.projectDir, '../../../node_modules/react-native-splash-screen/android') -include ':react-native-version-check' -project(':react-native-version-check').projectDir = new File(rootProject.projectDir, '../../../node_modules/react-native-version-check/android') -include ':react-native-android-open-settings' -project(':react-native-android-open-settings').projectDir = new File(rootProject.projectDir, '../../../node_modules/react-native-android-open-settings/android') -include ':react-native-udp' -project(':react-native-udp').projectDir = new File(rootProject.projectDir, '../../../node_modules/react-native-udp/android') -include ':react-native-localize' -project(':react-native-localize').projectDir = new File(rootProject.projectDir, '../../../node_modules/react-native-localize/android') -include ':react-native-config' -project(':react-native-config').projectDir = new File(rootProject.projectDir, '../../../node_modules/react-native-config/android') -include ':react-native-firebase' -project(':react-native-firebase').projectDir = new File(rootProject.projectDir, '../../../node_modules/react-native-firebase/android') -include ':react-native-tcp' -project(':react-native-tcp').projectDir = new File(rootProject.projectDir, '../../../node_modules/react-native-tcp/android') -include ':react-native-sentry' -project(':react-native-sentry').projectDir = new File(rootProject.projectDir, '../../../node_modules/react-native-sentry/android') -include ':react-native-secure-randombytes' -project(':react-native-secure-randombytes').projectDir = new File(rootProject.projectDir, '../../../node_modules/react-native-secure-randombytes/android') -include ':react-native-svg' -project(':react-native-svg').projectDir = new File(rootProject.projectDir, '../../../node_modules/react-native-svg/android') -include ':react-native-contacts' -project(':react-native-contacts').projectDir = new File(rootProject.projectDir, '../../../node_modules/react-native-contacts/android') -include ':react-native-keep-awake' -project(':react-native-keep-awake').projectDir = new File(rootProject.projectDir, '../../../node_modules/react-native-keep-awake/android') -include ':react-native-device-info' -project(':react-native-device-info').projectDir = new File(rootProject.projectDir, '../../../node_modules/react-native-device-info/android') -include ':react-native-fs' -project(':react-native-fs').projectDir = new File(rootProject.projectDir, '../../../node_modules/react-native-fs/android') +apply from: file("../../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings) include ':react-native-geth' project(':react-native-geth').projectDir = new File(rootProject.projectDir, '../../../node_modules/react-native-geth/android') -include ':react-native-flag-secure-android' -project(':react-native-flag-secure-android').projectDir = new File(rootProject.projectDir, '../../../node_modules/react-native-flag-secure-android/android') include ':react-native-confirm-device-credentials' project(':react-native-confirm-device-credentials').projectDir = new File(rootProject.projectDir, '../../../node_modules/react-native-confirm-device-credentials/android') -include ':react-native-restart-android' -project(':react-native-restart-android').projectDir = new File(rootProject.projectDir, '../../../node_modules/react-native-restart-android/android') -include ':react-native-sms-retriever' -project(':react-native-sms-retriever').projectDir = new File(rootProject.projectDir, '../../../node_modules/@celo/react-native-sms-retriever/android') -include ':react-native-share' -project(':react-native-share').projectDir = new File(rootProject.projectDir, '../../../node_modules/react-native-share/android') -include ':react-native-secure-key-store' -project(':react-native-secure-key-store').projectDir = new File(rootProject.projectDir, '../../../node_modules/react-native-secure-key-store/android') -include ':react-native-safe-area-context' -project(':react-native-safe-area-context').projectDir = new File(rootProject.projectDir, '../../../node_modules/react-native-safe-area-context/android') include ':app' diff --git a/packages/mobile/fastlane/Fastfile b/packages/mobile/fastlane/Fastfile index 935c56e1a09..5c97c255b1c 100644 --- a/packages/mobile/fastlane/Fastfile +++ b/packages/mobile/fastlane/Fastfile @@ -36,19 +36,23 @@ platform :android do desc 'Build the Android application - requires environment param' lane :build do |options| + clean sh('yarn', 'run', 'build:sdk', options[:sdkEnv]) environment = options[:environment].capitalize ENV["GRADLE_OPTS"] = '-Dorg.gradle.daemon=false -Dorg.gradle.jvmargs="-Xmx256m -XX:+HeapDumpOnOutOfMemoryError"' gradle(task: 'bundle' + environment + 'JsAndAssets', project_dir: 'android/') ENV["GRADLE_OPTS"] = '-Dorg.gradle.daemon=false -Dorg.gradle.jvmargs="-Xmx3500m -XX:+HeapDumpOnOutOfMemoryError"' - gradle(task: 'bundle', build_type: environment, project_dir: 'android/', flags: '-x bundle' + environment + 'JsAndAssets') + if options[:buildApk] + gradle(task: 'assemble', build_type: environment, project_dir: 'android/', flags: '-x bundle' + environment + 'JsAndAssets') + else + gradle(task: 'bundle', build_type: environment, project_dir: 'android/', flags: '-x bundle' + environment + 'JsAndAssets') + end end desc 'Ship Integration to Playstore Internal' lane :integration do env = 'integration' sdkEnv = 'integration' - clean build(environment: env, sdkEnv: sdkEnv) fastlane_supply(env, 'internal', env) end @@ -57,7 +61,6 @@ platform :android do lane :staging do env = 'staging' sdkEnv = 'alfajoresstaging' - clean build(environment: env, sdkEnv: sdkEnv) fastlane_supply(env, 'internal', env) end @@ -66,7 +69,6 @@ platform :android do lane :production do env = 'release' sdkEnv = 'argentinaproduction' - clean build(environment: env, sdkEnv: sdkEnv) fastlane_supply(env, 'alpha', 'production') end @@ -75,7 +77,6 @@ platform :android do lane :alfajores do env = 'alfajores' sdkEnv = 'alfajores' - clean build(environment: env, sdkEnv: sdkEnv) fastlane_supply(env, 'internal', env) end @@ -84,10 +85,23 @@ platform :android do lane :pilotapp do env = 'pilot' sdkEnv = 'pilot' - clean build(environment: env, sdkEnv: sdkEnv) fastlane_supply(env, 'internal', env) end + + desc 'Build an Android apk' + lane :build_apk do |options| + env = options[:env] + sdkEnv = options[:sdkEnv] + build(environment: env, sdkEnv: sdkEnv, buildApk: true) + end + + desc 'Build an Android bundle' + lane :build_bundle do |options| + env = options[:env] + sdkEnv = options[:sdkEnv] + build(environment: env, sdkEnv: sdkEnv) + end end diff --git a/packages/mobile/fastlane/README.md b/packages/mobile/fastlane/README.md index 58877c7e308..93fc7aef754 100644 --- a/packages/mobile/fastlane/README.md +++ b/packages/mobile/fastlane/README.md @@ -76,6 +76,22 @@ fastlane android pilotapp Ship Pilot to Playstore Internal +### android build_apk + +``` +fastlane android build_apk +``` + +Build an Android apk + +### android build_bundle + +``` +fastlane android build_bundle +``` + +Build an Android bundle + --- This README.md is auto-generated and will be re-generated every time [fastlane](https://fastlane.tools) is run. diff --git a/packages/mobile/fastlane/metadata/android/en-US/full_description.txt b/packages/mobile/fastlane/metadata/android/en-US/full_description.txt index 10966a59292..9ecab1cf2d6 100644 --- a/packages/mobile/fastlane/metadata/android/en-US/full_description.txt +++ b/packages/mobile/fastlane/metadata/android/en-US/full_description.txt @@ -1,5 +1,5 @@ -TESTNET INFORMATION & PRIVACY STATEMENT -This application connects to ‘Alfajores’, a test network running the Celo Protocol. Any balance amounts on this network are not redeemable for real value. The Alfajores Network stores phone number hashes to allow for payments to a phone number. By verifying your phone number, you consent to having your hashed phone number stored on the Celo Blockchain. +NETWORK INFORMATION & PRIVACY STATEMENT +This application connects to ‘Alfajores’, a development network running the Celo Protocol. Any balance amounts on this network are not redeemable for real value. The Alfajores Network stores phone number hashes to allow for payments to a phone number. By verifying your phone number, you consent to having your hashed phone number stored on the Celo Blockchain. To learn more, please read our Code of Conduct and Terms diff --git a/packages/mobile/fastlane/metadata/android/es-419/full_description.txt b/packages/mobile/fastlane/metadata/android/es-419/full_description.txt index 50d5d03c45d..c2f4069e5d8 100644 --- a/packages/mobile/fastlane/metadata/android/es-419/full_description.txt +++ b/packages/mobile/fastlane/metadata/android/es-419/full_description.txt @@ -1,5 +1,5 @@ -INFORMACIÓN DE TESTNET Y DECLARACIÓN DE PRIVACIDAD -Esta aplicación se conecta a 'Alfajores', una red de prueba que ejecuta el Protocolo Celo. Cualquier saldo en esta red no se puede canjear por valor real. La Red de Alfajores almacena hashes de números de teléfono para permitir pagos a un número de teléfono. Al verificar tu número de teléfono, usted acepta que el hash de tu número de teléfono se almacene en la Blockchain de Celo. +INFORMACIÓN DE LA RED Y DECLARACIÓN DE PRIVACIDAD +Esta aplicación se conecta a 'Alfajores', una red de desarrollo que ejecuta el Protocolo Celo. Cualquier saldo en esta red no se puede canjear por valor real. La Red Alfajores almacena hashes de números de teléfono para permitir pagos a un número de teléfono. Al verificar su número de teléfono, usted acepta que el hash de su número de teléfono se almacene en la Blockchain de Celo. Para obtener más información, lea nuestro Código de conducta y Términos de Servicio diff --git a/packages/mobile/index.js b/packages/mobile/index.js index 46bbce686d1..6de3dbacf65 100644 --- a/packages/mobile/index.js +++ b/packages/mobile/index.js @@ -4,7 +4,7 @@ import { AppRegistry } from 'react-native' import Logger from 'src/utils/Logger' import App from 'src/app/App' import { installSentry } from 'src/sentry/Sentry' -import { Sentry } from 'react-native-sentry' +import * as Sentry from '@sentry/react-native' import { onBackgroundNotification } from 'src/firebase/firebase' // Set this to true, if you are modifying Sentry and want to test your changes diff --git a/packages/mobile/ios/.gitignore b/packages/mobile/ios/.gitignore new file mode 100644 index 00000000000..8d2e78cad4c --- /dev/null +++ b/packages/mobile/ios/.gitignore @@ -0,0 +1,3 @@ +**/GoogleService-Info*.plist +sentry.properties +Pods \ No newline at end of file diff --git a/packages/mobile/ios/Podfile b/packages/mobile/ios/Podfile index 896d6a1d5a8..521e6466b0e 100644 --- a/packages/mobile/ios/Podfile +++ b/packages/mobile/ios/Podfile @@ -1,81 +1,66 @@ # File contents of "ios/Podfile" -platform :ios, '9.0' +platform :ios, "9.0" +require_relative "../../../node_modules/@react-native-community/cli-platform-ios/native_modules" -target 'celo' do +target "celo" do use_frameworks! - pod 'React', :path => '../node_modules/react-native', :subspecs => [ - 'Core', - 'CxxBridge', - 'DevSupport', - # the following ones are the ones taken from "Libraries" in Xcode: - 'RCTAnimation', - 'RCTActionSheet', - 'RCTBlob', - 'RCTGeolocation', - 'RCTImage', - 'RCTLinkingIOS', - 'RCTNetwork', - 'RCTSettings', - 'RCTText', - 'RCTVibration', - 'RCTWebSocket' - ] + # React Native Core pods + pod "FBLazyVector", :path => "../../../node_modules/react-native/Libraries/FBLazyVector" + pod "FBReactNativeSpec", :path => "../../../node_modules/react-native/Libraries/FBReactNativeSpec" + pod "RCTRequired", :path => "../../../node_modules/react-native/Libraries/RCTRequired" + pod "RCTTypeSafety", :path => "../../../node_modules/react-native/Libraries/TypeSafety" + pod "React", :path => "../../../node_modules/react-native/" + pod "React-Core", :path => "../../../node_modules/react-native/" + pod "React-CoreModules", :path => "../../../node_modules/react-native/React/CoreModules" + pod "React-Core/DevSupport", :path => "../../../node_modules/react-native/" + pod "React-RCTActionSheet", :path => "../../../node_modules/react-native/Libraries/ActionSheetIOS" + pod "React-RCTAnimation", :path => "../../../node_modules/react-native/Libraries/NativeAnimation" + pod "React-RCTBlob", :path => "../../../node_modules/react-native/Libraries/Blob" + pod "React-RCTImage", :path => "../../../node_modules/react-native/Libraries/Image" + pod "React-RCTLinking", :path => "../../../node_modules/react-native/Libraries/LinkingIOS" + pod "React-RCTNetwork", :path => "../../../node_modules/react-native/Libraries/Network" + pod "React-RCTSettings", :path => "../../../node_modules/react-native/Libraries/Settings" + pod "React-RCTText", :path => "../../../node_modules/react-native/Libraries/Text" + pod "React-RCTVibration", :path => "../../../node_modules/react-native/Libraries/Vibration" + pod "React-Core/RCTWebSocket", :path => "../../../node_modules/react-native/" + pod "React-cxxreact", :path => "../../../node_modules/react-native/ReactCommon/cxxreact" + pod "React-jsi", :path => "../../../node_modules/react-native/ReactCommon/jsi" + pod "React-jsiexecutor", :path => "../../../node_modules/react-native/ReactCommon/jsiexecutor" + pod "React-jsinspector", :path => "../../../node_modules/react-native/ReactCommon/jsinspector" + pod "ReactCommon/jscallinvoker", :path => "../../../node_modules/react-native/ReactCommon" + pod "ReactCommon/turbomodule/core", :path => "../../../node_modules/react-native/ReactCommon" + pod "Yoga", :path => "../../../node_modules/react-native/ReactCommon/yoga" + pod "DoubleConversion", :podspec => "../../../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec" + pod "glog", :podspec => "../../../node_modules/react-native/third-party-podspecs/glog.podspec" + pod "Folly", :podspec => "../../../node_modules/react-native/third-party-podspecs/Folly.podspec" - # the following dependencies are dependencies of React native itself. - pod 'yoga', :path => '../node_modules/react-native/ReactCommon/yoga/Yoga.podspec' - pod 'DoubleConversion', :podspec => '../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec' - pod 'Folly', :podspec => '../node_modules/react-native/third-party-podspecs/Folly.podspec' - pod 'glog', :podspec => '../node_modules/react-native/third-party-podspecs/GLog.podspec' + pod "react-native-geth", :path => "../../../node_modules/react-native-geth" + pod "CeloBlockchain", :path => "../../../node_modules/@celo/client/CeloBlockchain.podspec" - # React Native Libraries - # pod 'RNFBApp', :path => '../node_modules/@react-native-firebase/app' - # pod 'RNFBAuth', :path => '../node_modules/@react-native-firebase/auth' - # pod 'RNFBDatabase', :path => '../node_modules/@react-native-firebase/database' - pod 'RNFirebase', :path => '../../../node_modules/react-native-firebase/ios' - pod 'RNFS', :path => '../../../node_modules/react-native-fs' - pod 'react-native-geth', :path => '../../../node_modules/react-native-geth' - pod 'RNSVG', :path => '../../../node_modules/react-native-svg' - pod 'RNAnalytics', :path => '../../../node_modules/@segment/analytics-react-native' - pod 'RNAnalyticsIntegration-Firebase', :path => '../../../node_modules/@segment/analytics-react-native-firebase' - pod 'react-native-config', :path => '../../../node_modules/react-native-config' - pod 'react-native-contacts', :path => '../../../node_modules/react-native-contacts' - pod 'RNDeviceInfo', :path => '../../../node_modules/react-native-device-info' - pod 'react-native-keep-awake', :path => '../../../node_modules/react-native-keep-awake' - pod 'RNLocalize', :path => '../../../node_modules/react-native-localize' - pod 'react-native-mail', :path => '../../../node_modules/react-native-mail' - pod 'RNScreens', :path => '../../../node_modules/react-native-screens' - pod 'SentryReactNative', :path => '../../../node_modules/react-native-sentry' - pod 'react-native-splash-screen', :path => '../../../node_modules/react-native-splash-screen' - pod 'react-native-version-check', :path => '../../../node_modules/react-native-version-check' - pod 'RNRandomBytes', :path => '../../../node_modules/react-native-secure-randombytes' - pod 'react-native-tcp', :path => '../../../node_modules/react-native-tcp' - pod 'react-native-udp', :path => '../../../node_modules/react-native-udp' - pod 'react-native-netinfo', :path => '../../../node_modules/@react-native-community/netinfo' - pod 'RNShare', :path => '../../../node_modules/react-native-share' - pod 'react-native-camera', :path => '../../../node_modules/react-native-camera' - pod 'RNGestureHandler', :path => '../node_modules/react-native-gesture-handler' - pod 'CeloBlockchain', :path => '../../../node_modules/@celo/client/CeloBlockchain.podspec' - pod 'RNSecureKeyStore', :path => '../../../node_modules/react-native-secure-key-store/ios' - pod 'react-native-safe-area-context', :path => '../../../node_modules/react-native-safe-area-context' + pod "Firebase/Core", "~> 5.20.2" + pod "Firebase/Auth" + pod "Firebase/Storage" + pod "Firebase/Messaging" + pod "Firebase/Database" + pod "GoogleUtilities", "~> 5.3.7" - pod 'Firebase/Core', '~> 5.20.2' - pod 'Firebase/Auth' - pod 'Firebase/Storage' - pod 'Firebase/Messaging' - pod 'Firebase/Database' - pod 'GoogleUtilities', '~> 5.3.7' - # pod 'Segment-Firebase', '~> 2.4.0' - - target 'celoTests' do + target "celoTests" do inherit! :search_paths end + + use_native_modules! end post_install do |installer| installer.pods_project.targets.each do |target| target.build_configurations.each do |config| - config.build_settings['ENABLE_BITCODE'] = 'NO' + config.build_settings["ENABLE_BITCODE"] = "NO" end end + + rnfirebase = installer.pods_project.targets.find { |target| target.name == "RNFirebase" } + rnfirebase.build_configurations.each do |config| + config.build_settings["HEADER_SEARCH_PATHS"] = "$(inherited) ${PODS_ROOT}/Headers/Public/**" + end end diff --git a/packages/mobile/ios/Podfile.lock b/packages/mobile/ios/Podfile.lock index a5429a024de..36c9c5d9c5d 100644 --- a/packages/mobile/ios/Podfile.lock +++ b/packages/mobile/ios/Podfile.lock @@ -1,11 +1,19 @@ PODS: - Analytics (3.7.0) - boost-for-react-native (1.63.0) - - CeloBlockchain (0.0.156) + - CeloBlockchain (0.0.173) - Crashlytics (3.13.4): - Fabric (~> 1.10.2) - DoubleConversion (1.1.6) - Fabric (1.10.2) + - FBLazyVector (0.61.2) + - FBReactNativeSpec (0.61.2): + - Folly (= 2018.10.22.00) + - RCTRequired (= 0.61.2) + - RCTTypeSafety (= 0.61.2) + - React-Core (= 0.61.2) + - React-jsi (= 0.61.2) + - ReactCommon/turbomodule/core (= 0.61.2) - Firebase/Auth (5.20.2): - Firebase/CoreOnly - FirebaseAuth (= 5.4.2) @@ -32,7 +40,7 @@ PODS: - GoogleUtilities/Network (~> 5.2) - "GoogleUtilities/NSData+zlib (~> 5.2)" - nanopb (~> 0.3) - - FirebaseAnalyticsInterop (1.4.0) + - FirebaseAnalyticsInterop (1.3.0) - FirebaseAuth (5.4.2): - FirebaseAuthInterop (~> 1.0) - FirebaseCore (~> 5.2) @@ -63,6 +71,11 @@ PODS: - FirebaseCore (~> 5.2) - GTMSessionFetcher/Core (~> 1.1) - Folly (2018.10.22.00): + - boost-for-react-native + - DoubleConversion + - Folly/Default (= 2018.10.22.00) + - glog + - Folly/Default (2018.10.22.00): - boost-for-react-native - DoubleConversion - glog @@ -105,160 +118,328 @@ PODS: - GoogleUtilities/UserDefaults (5.3.7): - GoogleUtilities/Logger - GTMSessionFetcher/Core (1.2.2) - - leveldb-library (1.22) + - leveldb-library (1.20) - nanopb (0.3.901): - nanopb/decode (= 0.3.901) - nanopb/encode (= 0.3.901) - nanopb/decode (0.3.901) - nanopb/encode (0.3.901) - - Protobuf (3.9.2) - - React (0.59.10): - - React/Core (= 0.59.10) - - react-native-camera (2.9.0): + - Protobuf (3.9.0) + - RCTRequired (0.61.2) + - RCTTypeSafety (0.61.2): + - FBLazyVector (= 0.61.2) + - Folly (= 2018.10.22.00) + - RCTRequired (= 0.61.2) + - React-Core (= 0.61.2) + - React (0.61.2): + - React-Core (= 0.61.2) + - React-Core/DevSupport (= 0.61.2) + - React-Core/RCTWebSocket (= 0.61.2) + - React-RCTActionSheet (= 0.61.2) + - React-RCTAnimation (= 0.61.2) + - React-RCTBlob (= 0.61.2) + - React-RCTImage (= 0.61.2) + - React-RCTLinking (= 0.61.2) + - React-RCTNetwork (= 0.61.2) + - React-RCTSettings (= 0.61.2) + - React-RCTText (= 0.61.2) + - React-RCTVibration (= 0.61.2) + - React-Core (0.61.2): + - Folly (= 2018.10.22.00) + - glog + - React-Core/Default (= 0.61.2) + - React-cxxreact (= 0.61.2) + - React-jsi (= 0.61.2) + - React-jsiexecutor (= 0.61.2) + - Yoga + - React-Core/CoreModulesHeaders (0.61.2): + - Folly (= 2018.10.22.00) + - glog + - React-Core/Default + - React-cxxreact (= 0.61.2) + - React-jsi (= 0.61.2) + - React-jsiexecutor (= 0.61.2) + - Yoga + - React-Core/Default (0.61.2): + - Folly (= 2018.10.22.00) + - glog + - React-cxxreact (= 0.61.2) + - React-jsi (= 0.61.2) + - React-jsiexecutor (= 0.61.2) + - Yoga + - React-Core/DevSupport (0.61.2): + - Folly (= 2018.10.22.00) + - glog + - React-Core/Default (= 0.61.2) + - React-Core/RCTWebSocket (= 0.61.2) + - React-cxxreact (= 0.61.2) + - React-jsi (= 0.61.2) + - React-jsiexecutor (= 0.61.2) + - React-jsinspector (= 0.61.2) + - Yoga + - React-Core/RCTActionSheetHeaders (0.61.2): + - Folly (= 2018.10.22.00) + - glog + - React-Core/Default + - React-cxxreact (= 0.61.2) + - React-jsi (= 0.61.2) + - React-jsiexecutor (= 0.61.2) + - Yoga + - React-Core/RCTAnimationHeaders (0.61.2): + - Folly (= 2018.10.22.00) + - glog + - React-Core/Default + - React-cxxreact (= 0.61.2) + - React-jsi (= 0.61.2) + - React-jsiexecutor (= 0.61.2) + - Yoga + - React-Core/RCTBlobHeaders (0.61.2): + - Folly (= 2018.10.22.00) + - glog + - React-Core/Default + - React-cxxreact (= 0.61.2) + - React-jsi (= 0.61.2) + - React-jsiexecutor (= 0.61.2) + - Yoga + - React-Core/RCTImageHeaders (0.61.2): + - Folly (= 2018.10.22.00) + - glog + - React-Core/Default + - React-cxxreact (= 0.61.2) + - React-jsi (= 0.61.2) + - React-jsiexecutor (= 0.61.2) + - Yoga + - React-Core/RCTLinkingHeaders (0.61.2): + - Folly (= 2018.10.22.00) + - glog + - React-Core/Default + - React-cxxreact (= 0.61.2) + - React-jsi (= 0.61.2) + - React-jsiexecutor (= 0.61.2) + - Yoga + - React-Core/RCTNetworkHeaders (0.61.2): + - Folly (= 2018.10.22.00) + - glog + - React-Core/Default + - React-cxxreact (= 0.61.2) + - React-jsi (= 0.61.2) + - React-jsiexecutor (= 0.61.2) + - Yoga + - React-Core/RCTSettingsHeaders (0.61.2): + - Folly (= 2018.10.22.00) + - glog + - React-Core/Default + - React-cxxreact (= 0.61.2) + - React-jsi (= 0.61.2) + - React-jsiexecutor (= 0.61.2) + - Yoga + - React-Core/RCTTextHeaders (0.61.2): + - Folly (= 2018.10.22.00) + - glog + - React-Core/Default + - React-cxxreact (= 0.61.2) + - React-jsi (= 0.61.2) + - React-jsiexecutor (= 0.61.2) + - Yoga + - React-Core/RCTVibrationHeaders (0.61.2): + - Folly (= 2018.10.22.00) + - glog + - React-Core/Default + - React-cxxreact (= 0.61.2) + - React-jsi (= 0.61.2) + - React-jsiexecutor (= 0.61.2) + - Yoga + - React-Core/RCTWebSocket (0.61.2): + - Folly (= 2018.10.22.00) + - glog + - React-Core/Default (= 0.61.2) + - React-cxxreact (= 0.61.2) + - React-jsi (= 0.61.2) + - React-jsiexecutor (= 0.61.2) + - Yoga + - React-CoreModules (0.61.2): + - FBReactNativeSpec (= 0.61.2) + - Folly (= 2018.10.22.00) + - RCTTypeSafety (= 0.61.2) + - React-Core/CoreModulesHeaders (= 0.61.2) + - React-RCTImage (= 0.61.2) + - ReactCommon/turbomodule/core (= 0.61.2) + - React-cxxreact (0.61.2): + - boost-for-react-native (= 1.63.0) + - DoubleConversion + - Folly (= 2018.10.22.00) + - glog + - React-jsinspector (= 0.61.2) + - React-jsi (0.61.2): + - boost-for-react-native (= 1.63.0) + - DoubleConversion + - Folly (= 2018.10.22.00) + - glog + - React-jsi/Default (= 0.61.2) + - React-jsi/Default (0.61.2): + - boost-for-react-native (= 1.63.0) + - DoubleConversion + - Folly (= 2018.10.22.00) + - glog + - React-jsiexecutor (0.61.2): + - DoubleConversion + - Folly (= 2018.10.22.00) + - glog + - React-cxxreact (= 0.61.2) + - React-jsi (= 0.61.2) + - React-jsinspector (0.61.2) + - react-native-camera (3.7.1): - React - - react-native-camera/RCT (= 2.9.0) - - react-native-camera/RN (= 2.9.0) - - react-native-camera/RCT (2.9.0): + - react-native-camera/RCT (= 3.7.1) + - react-native-camera/RN (= 3.7.1) + - react-native-camera/RCT (3.7.1): - React - - react-native-camera/RN (2.9.0): + - react-native-camera/RN (3.7.1): - React - react-native-config (0.11.7): - React - - react-native-contacts (4.0.2): + - react-native-contacts (5.0.0): - React - react-native-geth (0.1.0-development): - CeloBlockchain - React - - react-native-keep-awake (3.0.1): + - react-native-keep-awake (4.0.0): - React - - react-native-mail (3.0.7): + - react-native-mail (4.0.0): - React - - react-native-netinfo (2.0.4): + - react-native-netinfo (4.4.0): - React - - react-native-safe-area-context (0.3.6): + - react-native-safe-area-context (0.5.0): - React - - react-native-splash-screen (3.1.1): + - react-native-sms (1.9.0): + - React + - react-native-splash-screen (3.2.0): - React - react-native-tcp (3.3.0): - React - react-native-udp (2.6.1): - React - - react-native-version-check (3.0.2): + - react-native-version-check (3.3.0): - React - - React/Core (0.59.10): - - yoga (= 0.59.10.React) - - React/CxxBridge (0.59.10): - - Folly (= 2018.10.22.00) - - React/Core - - React/cxxreact - - React/jsiexecutor - - React/cxxreact (0.59.10): - - boost-for-react-native (= 1.63.0) - - DoubleConversion - - Folly (= 2018.10.22.00) - - glog - - React/jsinspector - - React/DevSupport (0.59.10): - - React/Core - - React/RCTWebSocket - - React/fishhook (0.59.10) - - React/jsi (0.59.10): + - react-native-webview (7.4.1): + - React + - React-RCTActionSheet (0.61.2): + - React-Core/RCTActionSheetHeaders (= 0.61.2) + - React-RCTAnimation (0.61.2): + - React-Core/RCTAnimationHeaders (= 0.61.2) + - React-RCTBlob (0.61.2): + - React-Core/RCTBlobHeaders (= 0.61.2) + - React-Core/RCTWebSocket (= 0.61.2) + - React-jsi (= 0.61.2) + - React-RCTNetwork (= 0.61.2) + - React-RCTImage (0.61.2): + - React-Core/RCTImageHeaders (= 0.61.2) + - React-RCTNetwork (= 0.61.2) + - React-RCTLinking (0.61.2): + - React-Core/RCTLinkingHeaders (= 0.61.2) + - React-RCTNetwork (0.61.2): + - React-Core/RCTNetworkHeaders (= 0.61.2) + - React-RCTSettings (0.61.2): + - React-Core/RCTSettingsHeaders (= 0.61.2) + - React-RCTText (0.61.2): + - React-Core/RCTTextHeaders (= 0.61.2) + - React-RCTVibration (0.61.2): + - React-Core/RCTVibrationHeaders (= 0.61.2) + - ReactCommon/jscallinvoker (0.61.2): - DoubleConversion - Folly (= 2018.10.22.00) - glog - - React/jsiexecutor (0.59.10): + - React-cxxreact (= 0.61.2) + - ReactCommon/turbomodule/core (0.61.2): - DoubleConversion - Folly (= 2018.10.22.00) - glog - - React/cxxreact - - React/jsi - - React/jsinspector (0.59.10) - - React/RCTActionSheet (0.59.10): - - React/Core - - React/RCTAnimation (0.59.10): - - React/Core - - React/RCTBlob (0.59.10): - - React/Core - - React/RCTGeolocation (0.59.10): - - React/Core - - React/RCTImage (0.59.10): - - React/Core - - React/RCTNetwork - - React/RCTLinkingIOS (0.59.10): - - React/Core - - React/RCTNetwork (0.59.10): - - React/Core - - React/RCTSettings (0.59.10): - - React/Core - - React/RCTText (0.59.10): - - React/Core - - React/RCTVibration (0.59.10): - - React/Core - - React/RCTWebSocket (0.59.10): - - React/Core - - React/fishhook - - React/RCTBlob - - RNAnalytics (1.1.0-beta.1): + - React-Core (= 0.61.2) + - React-cxxreact (= 0.61.2) + - React-jsi (= 0.61.2) + - ReactCommon/jscallinvoker (= 0.61.2) + - RNAnalytics (1.1.0-beta.2): - Analytics - React - - RNAnalyticsIntegration-Firebase (1.1.0-beta.1): + - RNAnalyticsIntegration-Firebase (1.1.0-beta.2): - Analytics - React - RNAnalytics - Segment-Firebase - - RNDeviceInfo (2.1.0): + - RNCAsyncStorage (1.6.2): + - React + - RNDeviceInfo (4.0.1): - React - - RNFirebase (5.5.4): + - RNExitApp (1.1.0): + - React + - RNFirebase (5.5.6): - Firebase/Core - React - - RNFirebase/Crashlytics (= 5.5.4) - - RNFirebase/Crashlytics (5.5.4): + - RNFirebase/Crashlytics (= 5.5.6) + - RNFirebase/Crashlytics (5.5.6): - Crashlytics - Fabric - Firebase/Core - React - - RNFS (2.12.1): + - RNFS (2.14.1): + - React + - RNGestureHandler (1.4.1): - React - - RNGestureHandler (1.1.0): + - RNLocalize (1.3.0): - React - - RNLocalize (1.2.1): + - RNPermissions (2.0.2): - React - - RNRandomBytes (2.2.3): + - RNRandomBytes (3.0.0): - React - - RNScreens (1.0.0-alpha.22): + - RNReanimated (1.3.0): + - React + - RNScreens (1.0.0-alpha.23): - React - RNSecureKeyStore (1.0.0): - React - - RNShare (1.1.3): + - RNSentry (1.0.7): + - React + - Sentry (~> 4.4.0) + - RNShare (2.0.0): - React - - RNSVG (9.8.4): + - RNSVG (9.11.1): - React - - Segment-Firebase (2.4.1): + - Segment-Firebase (2.4.0): - Analytics (~> 3.2) - Firebase/Core (~> 5.0) - - Segment-Firebase/Core (= 2.4.1) - - Segment-Firebase/Core (2.4.1): + - Segment-Firebase/Core (= 2.4.0) + - Segment-Firebase/Core (2.4.0): - Analytics (~> 3.2) - Firebase/Core (~> 5.0) - - Sentry (4.1.3): - - Sentry/Core (= 4.1.3) - - Sentry/Core (4.1.3) - - SentryReactNative (0.43.2): - - React - - Sentry (~> 4.1.3) - - yoga (0.59.10.React) + - Sentry (4.4.0): + - Sentry/Core (= 4.4.0) + - Sentry/Core (4.4.0) + - Yoga (1.14.0) DEPENDENCIES: - "CeloBlockchain (from `../../../node_modules/@celo/client/CeloBlockchain.podspec`)" - - DoubleConversion (from `../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`) + - DoubleConversion (from `../../../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`) + - FBLazyVector (from `../../../node_modules/react-native/Libraries/FBLazyVector`) + - FBReactNativeSpec (from `../../../node_modules/react-native/Libraries/FBReactNativeSpec`) - Firebase/Auth - Firebase/Core (~> 5.20.2) - Firebase/Database - Firebase/Messaging - Firebase/Storage - - Folly (from `../node_modules/react-native/third-party-podspecs/Folly.podspec`) - - glog (from `../node_modules/react-native/third-party-podspecs/GLog.podspec`) + - Folly (from `../../../node_modules/react-native/third-party-podspecs/Folly.podspec`) + - glog (from `../../../node_modules/react-native/third-party-podspecs/glog.podspec`) - GoogleUtilities (~> 5.3.7) + - RCTRequired (from `../../../node_modules/react-native/Libraries/RCTRequired`) + - RCTTypeSafety (from `../../../node_modules/react-native/Libraries/TypeSafety`) + - React (from `../../../node_modules/react-native/`) + - React-Core (from `../../../node_modules/react-native/`) + - React-Core/DevSupport (from `../../../node_modules/react-native/`) + - React-Core/RCTWebSocket (from `../../../node_modules/react-native/`) + - React-CoreModules (from `../../../node_modules/react-native/React/CoreModules`) + - React-cxxreact (from `../../../node_modules/react-native/ReactCommon/cxxreact`) + - React-jsi (from `../../../node_modules/react-native/ReactCommon/jsi`) + - React-jsiexecutor (from `../../../node_modules/react-native/ReactCommon/jsiexecutor`) + - React-jsinspector (from `../../../node_modules/react-native/ReactCommon/jsinspector`) - react-native-camera (from `../../../node_modules/react-native-camera`) - react-native-config (from `../../../node_modules/react-native-config`) - react-native-contacts (from `../../../node_modules/react-native-contacts`) @@ -267,38 +448,41 @@ DEPENDENCIES: - react-native-mail (from `../../../node_modules/react-native-mail`) - "react-native-netinfo (from `../../../node_modules/@react-native-community/netinfo`)" - react-native-safe-area-context (from `../../../node_modules/react-native-safe-area-context`) + - react-native-sms (from `../../../node_modules/react-native-sms`) - react-native-splash-screen (from `../../../node_modules/react-native-splash-screen`) - react-native-tcp (from `../../../node_modules/react-native-tcp`) - react-native-udp (from `../../../node_modules/react-native-udp`) - react-native-version-check (from `../../../node_modules/react-native-version-check`) - - React/Core (from `../node_modules/react-native`) - - React/CxxBridge (from `../node_modules/react-native`) - - React/DevSupport (from `../node_modules/react-native`) - - React/RCTActionSheet (from `../node_modules/react-native`) - - React/RCTAnimation (from `../node_modules/react-native`) - - React/RCTBlob (from `../node_modules/react-native`) - - React/RCTGeolocation (from `../node_modules/react-native`) - - React/RCTImage (from `../node_modules/react-native`) - - React/RCTLinkingIOS (from `../node_modules/react-native`) - - React/RCTNetwork (from `../node_modules/react-native`) - - React/RCTSettings (from `../node_modules/react-native`) - - React/RCTText (from `../node_modules/react-native`) - - React/RCTVibration (from `../node_modules/react-native`) - - React/RCTWebSocket (from `../node_modules/react-native`) + - react-native-webview (from `../../../node_modules/react-native-webview`) + - React-RCTActionSheet (from `../../../node_modules/react-native/Libraries/ActionSheetIOS`) + - React-RCTAnimation (from `../../../node_modules/react-native/Libraries/NativeAnimation`) + - React-RCTBlob (from `../../../node_modules/react-native/Libraries/Blob`) + - React-RCTImage (from `../../../node_modules/react-native/Libraries/Image`) + - React-RCTLinking (from `../../../node_modules/react-native/Libraries/LinkingIOS`) + - React-RCTNetwork (from `../../../node_modules/react-native/Libraries/Network`) + - React-RCTSettings (from `../../../node_modules/react-native/Libraries/Settings`) + - React-RCTText (from `../../../node_modules/react-native/Libraries/Text`) + - React-RCTVibration (from `../../../node_modules/react-native/Libraries/Vibration`) + - ReactCommon/jscallinvoker (from `../../../node_modules/react-native/ReactCommon`) + - ReactCommon/turbomodule/core (from `../../../node_modules/react-native/ReactCommon`) - "RNAnalytics (from `../../../node_modules/@segment/analytics-react-native`)" - "RNAnalyticsIntegration-Firebase (from `../../../node_modules/@segment/analytics-react-native-firebase`)" + - "RNCAsyncStorage (from `../../../node_modules/@react-native-community/async-storage`)" - RNDeviceInfo (from `../../../node_modules/react-native-device-info`) + - RNExitApp (from `../../../node_modules/react-native-exit-app`) - RNFirebase (from `../../../node_modules/react-native-firebase/ios`) - RNFS (from `../../../node_modules/react-native-fs`) - - RNGestureHandler (from `../node_modules/react-native-gesture-handler`) + - RNGestureHandler (from `../../../node_modules/react-native-gesture-handler`) - RNLocalize (from `../../../node_modules/react-native-localize`) + - RNPermissions (from `../../../node_modules/react-native-permissions`) - RNRandomBytes (from `../../../node_modules/react-native-secure-randombytes`) + - RNReanimated (from `../../../node_modules/react-native-reanimated`) - RNScreens (from `../../../node_modules/react-native-screens`) - RNSecureKeyStore (from `../../../node_modules/react-native-secure-key-store/ios`) + - "RNSentry (from `../../../node_modules/@sentry/react-native`)" - RNShare (from `../../../node_modules/react-native-share`) - RNSVG (from `../../../node_modules/react-native-svg`) - - SentryReactNative (from `../../../node_modules/react-native-sentry`) - - yoga (from `../node_modules/react-native/ReactCommon/yoga/Yoga.podspec`) + - Yoga (from `../../../node_modules/react-native/ReactCommon/yoga`) SPEC REPOS: https://github.com/cocoapods/specs.git: @@ -329,13 +513,33 @@ EXTERNAL SOURCES: CeloBlockchain: :path: "../../../node_modules/@celo/client/CeloBlockchain.podspec" DoubleConversion: - :podspec: "../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec" + :podspec: "../../../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec" + FBLazyVector: + :path: "../../../node_modules/react-native/Libraries/FBLazyVector" + FBReactNativeSpec: + :path: "../../../node_modules/react-native/Libraries/FBReactNativeSpec" Folly: - :podspec: "../node_modules/react-native/third-party-podspecs/Folly.podspec" + :podspec: "../../../node_modules/react-native/third-party-podspecs/Folly.podspec" glog: - :podspec: "../node_modules/react-native/third-party-podspecs/GLog.podspec" + :podspec: "../../../node_modules/react-native/third-party-podspecs/glog.podspec" + RCTRequired: + :path: "../../../node_modules/react-native/Libraries/RCTRequired" + RCTTypeSafety: + :path: "../../../node_modules/react-native/Libraries/TypeSafety" React: - :path: "../node_modules/react-native" + :path: "../../../node_modules/react-native/" + React-Core: + :path: "../../../node_modules/react-native/" + React-CoreModules: + :path: "../../../node_modules/react-native/React/CoreModules" + React-cxxreact: + :path: "../../../node_modules/react-native/ReactCommon/cxxreact" + React-jsi: + :path: "../../../node_modules/react-native/ReactCommon/jsi" + React-jsiexecutor: + :path: "../../../node_modules/react-native/ReactCommon/jsiexecutor" + React-jsinspector: + :path: "../../../node_modules/react-native/ReactCommon/jsinspector" react-native-camera: :path: "../../../node_modules/react-native-camera" react-native-config: @@ -352,6 +556,8 @@ EXTERNAL SOURCES: :path: "../../../node_modules/@react-native-community/netinfo" react-native-safe-area-context: :path: "../../../node_modules/react-native-safe-area-context" + react-native-sms: + :path: "../../../node_modules/react-native-sms" react-native-splash-screen: :path: "../../../node_modules/react-native-splash-screen" react-native-tcp: @@ -360,45 +566,77 @@ EXTERNAL SOURCES: :path: "../../../node_modules/react-native-udp" react-native-version-check: :path: "../../../node_modules/react-native-version-check" + react-native-webview: + :path: "../../../node_modules/react-native-webview" + React-RCTActionSheet: + :path: "../../../node_modules/react-native/Libraries/ActionSheetIOS" + React-RCTAnimation: + :path: "../../../node_modules/react-native/Libraries/NativeAnimation" + React-RCTBlob: + :path: "../../../node_modules/react-native/Libraries/Blob" + React-RCTImage: + :path: "../../../node_modules/react-native/Libraries/Image" + React-RCTLinking: + :path: "../../../node_modules/react-native/Libraries/LinkingIOS" + React-RCTNetwork: + :path: "../../../node_modules/react-native/Libraries/Network" + React-RCTSettings: + :path: "../../../node_modules/react-native/Libraries/Settings" + React-RCTText: + :path: "../../../node_modules/react-native/Libraries/Text" + React-RCTVibration: + :path: "../../../node_modules/react-native/Libraries/Vibration" + ReactCommon: + :path: "../../../node_modules/react-native/ReactCommon" RNAnalytics: :path: "../../../node_modules/@segment/analytics-react-native" RNAnalyticsIntegration-Firebase: :path: "../../../node_modules/@segment/analytics-react-native-firebase" + RNCAsyncStorage: + :path: "../../../node_modules/@react-native-community/async-storage" RNDeviceInfo: :path: "../../../node_modules/react-native-device-info" + RNExitApp: + :path: "../../../node_modules/react-native-exit-app" RNFirebase: :path: "../../../node_modules/react-native-firebase/ios" RNFS: :path: "../../../node_modules/react-native-fs" RNGestureHandler: - :path: "../node_modules/react-native-gesture-handler" + :path: "../../../node_modules/react-native-gesture-handler" RNLocalize: :path: "../../../node_modules/react-native-localize" + RNPermissions: + :path: "../../../node_modules/react-native-permissions" RNRandomBytes: :path: "../../../node_modules/react-native-secure-randombytes" + RNReanimated: + :path: "../../../node_modules/react-native-reanimated" RNScreens: :path: "../../../node_modules/react-native-screens" RNSecureKeyStore: :path: "../../../node_modules/react-native-secure-key-store/ios" + RNSentry: + :path: "../../../node_modules/@sentry/react-native" RNShare: :path: "../../../node_modules/react-native-share" RNSVG: :path: "../../../node_modules/react-native-svg" - SentryReactNative: - :path: "../../../node_modules/react-native-sentry" - yoga: - :path: "../node_modules/react-native/ReactCommon/yoga/Yoga.podspec" + Yoga: + :path: "../../../node_modules/react-native/ReactCommon/yoga" SPEC CHECKSUMS: Analytics: 77fd5fb102a4a5eedafa2c2b0245ceb7b7c15e45 boost-for-react-native: 39c7adb57c4e60d6c5479dd8623128eb5b3f0f2c - CeloBlockchain: f258122010fb610057979254d4a3f88eb7fb0d21 + CeloBlockchain: 6c7de89c718d54424ad9f49a37372656d9833451 Crashlytics: 2dfd686bcb918dc10ee0e76f7f853fe42c7bd552 - DoubleConversion: bb338842f62ab1d708ceb63ec3d999f0f3d98ecd + DoubleConversion: 5805e889d232975c086db112ece9ed034df7a0b2 Fabric: 706c8b8098fff96c33c0db69cbf81f9c551d0d74 + FBLazyVector: 68b6a76960fbd8ecd9fb7ce0aadd3329c3340a99 + FBReactNativeSpec: 5a764c60abdc3336a213e5310c40b74741f32839 Firebase: 0c8cf33f266410c61ab3e2265cfa412200351d9c FirebaseAnalytics: ece1aa57a4f43c64d53a648b5a5e05151aae947b - FirebaseAnalyticsInterop: d48b6ab67bcf016a05e55b71fc39c61c0cb6b7f3 + FirebaseAnalyticsInterop: 0f250d71e8741352407a8db351e6de78c5c56d35 FirebaseAuth: dd7bbf03a5aee0eafb3a1aee4d2812bd74bac890 FirebaseAuthInterop: 0ffa57668be100582bb7643d4fcb7615496c41fc FirebaseCore: f1a9a8be1aee4bf71a2fc0f4096df6788bdfda61 @@ -406,44 +644,68 @@ SPEC CHECKSUMS: FirebaseInstanceID: a122b0c258720cf250551bb2bedf48c699f80d90 FirebaseMessaging: 4235f949ce1c4e827aeb19705ba5c53f9b85aa10 FirebaseStorage: 6162ef4322502b818d9de0ec552f5226d283de43 - Folly: de497beb10f102453a1afa9edbf8cf8a251890de - glog: aefd1eb5dda2ab95ba0938556f34b98e2da3a60d + Folly: 30e7936e1c45c08d884aa59369ed951a8e68cf51 + glog: 1f3da668190260b06b429bb211bfbee5cd790c28 GoogleAppMeasurement: ffe513e90551844a739e7bcbb1d2aca1c28a4338 GoogleUtilities: 111a012f4c3a29c9e7c954c082fafd6ee3c999c0 GTMSessionFetcher: 61bb0f61a4cb560030f1222021178008a5727a23 - leveldb-library: 55d93ee664b4007aac644a782d11da33fba316f7 + leveldb-library: 08cba283675b7ed2d99629a4bc5fd052cd2bb6a5 nanopb: 2901f78ea1b7b4015c860c2fdd1ea2fee1a18d48 - Protobuf: 67fb42ba613def994e61854de2b3164f13790cc4 - React: 36d0768f9e93be2473b37e7fa64f92c1d5341eef - react-native-camera: 571e4cfe70e7021e9077c1699a53e659db971f96 + Protobuf: 1097ca58584c8d9be81bfbf2c5ff5975648dd87a + RCTRequired: c639d59ed389cfb1f1203f65c2ea946d8ec586e2 + RCTTypeSafety: dc23fb655d6c77667c78e327bf661bc11e3b8aec + React: 7e586e5d7bec12b91c1a096826b0fc9ab1da7865 + React-Core: 8ddb9770b4a30a6ab4a754e6ed5ec76454e3d699 + React-CoreModules: b3d9eece8ad7df36c917a41f05c1168c52fe0b34 + React-cxxreact: 1f972757c0bd08d962ef78068e06613c27489a3f + React-jsi: 32285a21b1b24c36060493ed3057a34677d58d09 + React-jsiexecutor: 8909917ff7d8f21a57e443a866fd8d4560e50c65 + React-jsinspector: 111d7d342b07a904c400592e02a2b958f1098b60 + react-native-camera: ccbcbe683c621b9a4270e86e3a46bb9609121186 react-native-config: 8f6b2b9e017f6a5e92a97353c768e19e67294bb1 - react-native-contacts: 348958bb7da4399040b1eb45bfb7f6af08e61412 + react-native-contacts: d1a60e38dadb67dbbe481480338988e00966a30d react-native-geth: b643560e11512a3c0b1d78df929cb9f2409cf4a2 - react-native-keep-awake: abcf6d09d0cc5fe45df4a56a9382e25d10cff8b6 - react-native-mail: 021d8ee60e374609f5689ef354dc8e36839a9ba6 - react-native-netinfo: 1ea4efa22c02519ac8043ac3f000062a4e320795 - react-native-safe-area-context: e380a6f783ccaec848e2f3cc8eb205a62362950d - react-native-splash-screen: 353334c5ae82d8c74501ea7cbb916cb7cb20c8bf + react-native-keep-awake: afad8a51dfef9fe9655a6344771be32c8596d774 + react-native-mail: 7e37dfbe93ff0d4c7df346b738854dbed533e86f + react-native-netinfo: 6bb847e64f45a2d69c6173741111cfd95c669301 + react-native-safe-area-context: 13004a45f3021328fdd9ee1f987c3131fb65928d + react-native-sms: 6fc6a1581970f77253981c8c69cd0d303e1d991c + react-native-splash-screen: 200d11d188e2e78cea3ad319964f6142b6384865 react-native-tcp: e1a8c3ac010774cd71811989805ff3eaebb62f17 react-native-udp: 54a1aa9bf5c0824f930b1ba6dbfb3fd3e733bba9 - react-native-version-check: 901616b6d258b385628120441bd0b285b24c7be1 - RNAnalytics: 05243c1fa17186f07be3eae30514b43aeb2e0578 - RNAnalyticsIntegration-Firebase: 82b879019b012d1aa1cbcb6dfc466acd7233bb97 - RNDeviceInfo: 2b4f76adc479657bf37bfc927bbfb027b17515d4 - RNFirebase: 7d4733713a0f436d55388b55ca3744385c70fd2d - RNFS: 0f4d630b538e93e0e9e3519ff0b7b7b48760d8ed - RNGestureHandler: b65d391f4f570178d657b99a16ec99d09b8656b0 - RNLocalize: f42aea3133d91d7fa3ec01284a6895019af1a83f - RNRandomBytes: ed3e5894ef983d1afd17a63349cc517b260138fa - RNScreens: 720a9e6968beb73e8196239801e887d8401f86ed + react-native-version-check: 7d578f3405cd9d673bbfd1225f727c87991e9ef9 + react-native-webview: e800df5dd1a0f3555fd543ebf7d881a9014851f2 + React-RCTActionSheet: 89b037c0fb7d2671607cb645760164e7e0c013f6 + React-RCTAnimation: e3cefa93c38c004c318f7ec04b883eb14b8b8235 + React-RCTBlob: d26ac0e313fbf14e7203473fd593ccaaeee8329e + React-RCTImage: 4bdd9588783fa9e48ef669ccd4f747224e208edf + React-RCTLinking: 65f0088ff463babd3d5d567964a65b74141eff3b + React-RCTNetwork: 0c1a73576c1cfeafe68396556de1b17d93c0c595 + React-RCTSettings: 4194f1f0edbddf3fd44d1714dc6578bb20379b60 + React-RCTText: e3ef6191cdb627855ff7fe8fa0c1e14094967fb8 + React-RCTVibration: fb54c732fd20405a76598e431aa2f8c2bf527de9 + ReactCommon: 5848032ed2f274fcb40f6b9ec24067787c42d479 + RNAnalytics: 67854ebc95a440c6d522babd84cf1968c8629947 + RNAnalyticsIntegration-Firebase: e94d754cae94d5e0d520666c9452c14a35cebc06 + RNCAsyncStorage: 60a80e72d95bf02a01cace55d3697d9724f0d77f + RNDeviceInfo: 687c1b2ab6d86ff1ca1208783320cd144138c7f2 + RNExitApp: c4e052df2568b43bec8a37c7cd61194d4cfee2c3 + RNFirebase: ac0de8b24c6f91ae9459575491ed6a77327619c6 + RNFS: a8fbe7060fa49157d819466404794ad9c58e58cf + RNGestureHandler: 4cb47a93019c1a201df2644413a0a1569a51c8aa + RNLocalize: cebb57c3c1e5479806204ce135b26bdd79d17e8a + RNPermissions: 9525b0f4d209fdf9a373d52d1492ae2943e691e5 + RNRandomBytes: e5680396032547c8ceb494768532459dc6911b76 + RNReanimated: 6abbbae2e5e72609d85aabd92a982a94566885f1 + RNScreens: f28b48b8345f2f5f39ed6195518291515032a788 RNSecureKeyStore: f1ad870e53806453039f650720d2845c678d89c8 - RNShare: 4f206fa36e384e95a0cbf79f2a92490647e93127 - RNSVG: a78b4c581e33a1bb72968997d167afabf72b05dc - Segment-Firebase: 165b1be115ad2011a3e3f4312301e2c96f92ab38 - Sentry: 4e8a17b61ddd116f89536cc81d567fdee1ebca96 - SentryReactNative: 07237139c00366ea2e75ae3e5c566e7a71c27a90 - yoga: 684513b14b03201579ba3cee20218c9d1298b0cc + RNSentry: e57b6acd2424e185ba2e23ef78aa2186a40e83e0 + RNShare: 8b171d4b43c1d886917fdd303bf7a4b87167b05c + RNSVG: be27aa7c58819f97399388ae53d7fa0572f32c7f + Segment-Firebase: cac4b742228ef74d50ed886f3fc731e4bc8b7d29 + Sentry: 26650184fe71eb7476dfd2737acb5ea6cc64b4b1 + Yoga: 14927e37bd25376d216b150ab2a561773d57911f -PODFILE CHECKSUM: 02a0ed956675dc8aa46eab297cf94e8823e80147 +PODFILE CHECKSUM: 0d3a8018c6a563abba8559d74620bf8b8b370fe8 COCOAPODS: 1.7.5 diff --git a/packages/mobile/ios/celo-tvOS/Info.plist b/packages/mobile/ios/celo-tvOS/Info.plist index f841c2c0bd7..a6e6adb3716 100644 --- a/packages/mobile/ios/celo-tvOS/Info.plist +++ b/packages/mobile/ios/celo-tvOS/Info.plist @@ -15,11 +15,11 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.4.2 + 1.5.1 CFBundleSignature ???? CFBundleVersion - 7 + 14 LSRequiresIPhoneOS UILaunchStoryboardName diff --git a/packages/mobile/ios/celo-tvOSTests/Info.plist b/packages/mobile/ios/celo-tvOSTests/Info.plist index 18a4a24bfad..4d93072cfe2 100644 --- a/packages/mobile/ios/celo-tvOSTests/Info.plist +++ b/packages/mobile/ios/celo-tvOSTests/Info.plist @@ -15,10 +15,10 @@ CFBundlePackageType BNDL CFBundleShortVersionString - 1.4.2 + 1.5.1 CFBundleSignature ???? CFBundleVersion - 7 + 14 diff --git a/packages/mobile/ios/celo.xcodeproj/project.pbxproj b/packages/mobile/ios/celo.xcodeproj/project.pbxproj index abe887e2507..b1eb6be22a6 100644 --- a/packages/mobile/ios/celo.xcodeproj/project.pbxproj +++ b/packages/mobile/ios/celo.xcodeproj/project.pbxproj @@ -249,6 +249,7 @@ 13B07F8E1A680F5B00A75B9A /* Resources */, 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */, ED787910B2A9AE78802F0CBC /* [CP] Embed Pods Frameworks */, + FAA606D3D7E04ED7985FC9FF /* Upload Debug Symbols to Sentry */, ); buildRules = ( ); @@ -409,7 +410,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "# First set the path to sentry.properties\nexport SENTRY_PROPERTIES=sentry.properties\n\n# Setup nvm and set node\n[ -z \"$NVM_DIR\" ] && export NVM_DIR=\"$HOME/.nvm\"\n\nif [[ -s \"$HOME/.nvm/nvm.sh\" ]]; then\n. \"$HOME/.nvm/nvm.sh\"\nelif [[ -x \"$(command -v brew)\" && -s \"$(brew --prefix nvm)/nvm.sh\" ]]; then\n. \"$(brew --prefix nvm)/nvm.sh\"\nfi\n\n# Set up the nodenv node version manager if present\nif [[ -x \"$HOME/.nodenv/bin/nodenv\" ]]; then\neval \"$(\"$HOME/.nodenv/bin/nodenv\" init -)\"\nfi\n\n[ -z \"$NODE_BINARY\" ] && export NODE_BINARY=\"node\"\n\n$NODE_BINARY ../../../node_modules/@sentry/cli/bin/sentry-cli react-native xcode \\\n../node_modules/react-native/scripts/react-native-xcode.sh\n"; + shellScript = "# First set the path to sentry.properties\nexport SENTRY_PROPERTIES=sentry.properties\n\n# Setup nvm and set node\n[ -z \"$NVM_DIR\" ] && export NVM_DIR=\"$HOME/.nvm\"\n\nif [[ -s \"$HOME/.nvm/nvm.sh\" ]]; then\n. \"$HOME/.nvm/nvm.sh\"\nelif [[ -x \"$(command -v brew)\" && -s \"$(brew --prefix nvm)/nvm.sh\" ]]; then\n. \"$(brew --prefix nvm)/nvm.sh\"\nfi\n\n# Set up the nodenv node version manager if present\nif [[ -x \"$HOME/.nodenv/bin/nodenv\" ]]; then\neval \"$(\"$HOME/.nodenv/bin/nodenv\" init -)\"\nfi\n\n[ -z \"$NODE_BINARY\" ] && export NODE_BINARY=\"node\"\n\n$NODE_BINARY ../../../node_modules/@sentry/cli/bin/sentry-cli react-native xcode \\\n../../../node_modules/react-native/scripts/react-native-xcode.sh\n"; }; 2D02E4CB1E0B4B27006451C7 /* Bundle React Native Code And Images */ = { isa = PBXShellScriptBuildPhase; @@ -457,22 +458,44 @@ "${BUILT_PRODUCTS_DIR}/Analytics/Analytics.framework", "${BUILT_PRODUCTS_DIR}/CeloBlockchain/CeloBlockchain.framework", "${BUILT_PRODUCTS_DIR}/DoubleConversion/DoubleConversion.framework", + "${BUILT_PRODUCTS_DIR}/FBReactNativeSpec/FBReactNativeSpec.framework", "${BUILT_PRODUCTS_DIR}/Folly/folly.framework", "${BUILT_PRODUCTS_DIR}/GTMSessionFetcher/GTMSessionFetcher.framework", "${BUILT_PRODUCTS_DIR}/GoogleUtilities/GoogleUtilities.framework", "${BUILT_PRODUCTS_DIR}/Protobuf/Protobuf.framework", + "${BUILT_PRODUCTS_DIR}/RCTTypeSafety/RCTTypeSafety.framework", + "${BUILT_PRODUCTS_DIR}/RNCAsyncStorage/RNCAsyncStorage.framework", "${BUILT_PRODUCTS_DIR}/RNDeviceInfo/RNDeviceInfo.framework", + "${BUILT_PRODUCTS_DIR}/RNExitApp/RNExitApp.framework", "${BUILT_PRODUCTS_DIR}/RNFS/RNFS.framework", "${BUILT_PRODUCTS_DIR}/RNGestureHandler/RNGestureHandler.framework", "${BUILT_PRODUCTS_DIR}/RNLocalize/RNLocalize.framework", + "${BUILT_PRODUCTS_DIR}/RNPermissions/RNPermissions.framework", "${BUILT_PRODUCTS_DIR}/RNRandomBytes/RNRandomBytes.framework", + "${BUILT_PRODUCTS_DIR}/RNReanimated/RNReanimated.framework", "${BUILT_PRODUCTS_DIR}/RNSVG/RNSVG.framework", "${BUILT_PRODUCTS_DIR}/RNScreens/RNScreens.framework", "${BUILT_PRODUCTS_DIR}/RNSecureKeyStore/RNSecureKeyStore.framework", + "${BUILT_PRODUCTS_DIR}/RNSentry/RNSentry.framework", "${BUILT_PRODUCTS_DIR}/RNShare/RNShare.framework", - "${BUILT_PRODUCTS_DIR}/React/React.framework", + "${BUILT_PRODUCTS_DIR}/React-Core/React.framework", + "${BUILT_PRODUCTS_DIR}/React-CoreModules/CoreModules.framework", + "${BUILT_PRODUCTS_DIR}/React-RCTActionSheet/RCTActionSheet.framework", + "${BUILT_PRODUCTS_DIR}/React-RCTAnimation/RCTAnimation.framework", + "${BUILT_PRODUCTS_DIR}/React-RCTBlob/RCTBlob.framework", + "${BUILT_PRODUCTS_DIR}/React-RCTImage/RCTImage.framework", + "${BUILT_PRODUCTS_DIR}/React-RCTLinking/RCTLinking.framework", + "${BUILT_PRODUCTS_DIR}/React-RCTNetwork/RCTNetwork.framework", + "${BUILT_PRODUCTS_DIR}/React-RCTSettings/RCTSettings.framework", + "${BUILT_PRODUCTS_DIR}/React-RCTText/RCTText.framework", + "${BUILT_PRODUCTS_DIR}/React-RCTVibration/RCTVibration.framework", + "${BUILT_PRODUCTS_DIR}/React-cxxreact/cxxreact.framework", + "${BUILT_PRODUCTS_DIR}/React-jsi/jsi.framework", + "${BUILT_PRODUCTS_DIR}/React-jsiexecutor/jsireact.framework", + "${BUILT_PRODUCTS_DIR}/React-jsinspector/jsinspector.framework", + "${BUILT_PRODUCTS_DIR}/ReactCommon/ReactCommon.framework", "${BUILT_PRODUCTS_DIR}/Sentry/Sentry.framework", - "${BUILT_PRODUCTS_DIR}/SentryReactNative/SentryReactNative.framework", + "${BUILT_PRODUCTS_DIR}/Yoga/yoga.framework", "${BUILT_PRODUCTS_DIR}/glog/glog.framework", "${BUILT_PRODUCTS_DIR}/leveldb-library/leveldb.framework", "${BUILT_PRODUCTS_DIR}/nanopb/nanopb.framework", @@ -484,33 +507,56 @@ "${BUILT_PRODUCTS_DIR}/react-native-mail/react_native_mail.framework", "${BUILT_PRODUCTS_DIR}/react-native-netinfo/react_native_netinfo.framework", "${BUILT_PRODUCTS_DIR}/react-native-safe-area-context/react_native_safe_area_context.framework", + "${BUILT_PRODUCTS_DIR}/react-native-sms/react_native_sms.framework", "${BUILT_PRODUCTS_DIR}/react-native-splash-screen/react_native_splash_screen.framework", "${BUILT_PRODUCTS_DIR}/react-native-tcp/react_native_tcp.framework", "${BUILT_PRODUCTS_DIR}/react-native-udp/react_native_udp.framework", "${BUILT_PRODUCTS_DIR}/react-native-version-check/react_native_version_check.framework", - "${BUILT_PRODUCTS_DIR}/yoga/yoga.framework", + "${BUILT_PRODUCTS_DIR}/react-native-webview/react_native_webview.framework", ); name = "[CP] Embed Pods Frameworks"; outputPaths = ( "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Analytics.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/CeloBlockchain.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/DoubleConversion.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FBReactNativeSpec.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/folly.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GTMSessionFetcher.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleUtilities.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Protobuf.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RCTTypeSafety.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RNCAsyncStorage.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RNDeviceInfo.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RNExitApp.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RNFS.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RNGestureHandler.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RNLocalize.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RNPermissions.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RNRandomBytes.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RNReanimated.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RNSVG.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RNScreens.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RNSecureKeyStore.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RNSentry.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RNShare.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/React.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/CoreModules.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RCTActionSheet.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RCTAnimation.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RCTBlob.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RCTImage.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RCTLinking.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RCTNetwork.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RCTSettings.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RCTText.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RCTVibration.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/cxxreact.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/jsi.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/jsireact.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/jsinspector.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ReactCommon.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Sentry.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SentryReactNative.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/yoga.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/glog.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/leveldb.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/nanopb.framework", @@ -522,11 +568,12 @@ "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/react_native_mail.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/react_native_netinfo.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/react_native_safe_area_context.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/react_native_sms.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/react_native_splash_screen.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/react_native_tcp.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/react_native_udp.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/react_native_version_check.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/yoga.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/react_native_webview.framework", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; @@ -555,6 +602,20 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; + FAA606D3D7E04ED7985FC9FF /* Upload Debug Symbols to Sentry */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Upload Debug Symbols to Sentry"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "export SENTRY_PROPERTIES=sentry.properties\n\n# Setup nvm and set node\n[ -z \"$NVM_DIR\" ] && export NVM_DIR=\"$HOME/.nvm\"\n\nif [[ -s \"$HOME/.nvm/nvm.sh\" ]]; then\n. \"$HOME/.nvm/nvm.sh\"\nelif [[ -x \"$(command -v brew)\" && -s \"$(brew --prefix nvm)/nvm.sh\" ]]; then\n. \"$(brew --prefix nvm)/nvm.sh\"\nfi\n\n# Set up the nodenv node version manager if present\nif [[ -x \"$HOME/.nodenv/bin/nodenv\" ]]; then\neval \"$(\"$HOME/.nodenv/bin/nodenv\" init -)\"\nfi\n\n[ -z \"$NODE_BINARY\" ] && export NODE_BINARY=\"node\"\n\n$NODE_BINARY ../../../node_modules/@sentry/cli/bin/sentry-cli upload-dsym\n"; + }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -663,15 +724,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; LIBRARY_SEARCH_PATHS = ( "$(inherited)", - "\"$(SRCROOT)/$(TARGET_NAME)\"", - "\"$(SRCROOT)/$(TARGET_NAME)\"", - "\"$(SRCROOT)/$(TARGET_NAME)\"", - "\"$(SRCROOT)/$(TARGET_NAME)\"", - "\"$(SRCROOT)/$(TARGET_NAME)\"", - "\"$(SRCROOT)/$(TARGET_NAME)\"", - "\"$(SRCROOT)/$(TARGET_NAME)\"", - "\"$(SRCROOT)/$(TARGET_NAME)\"", - "\"$(SRCROOT)/$(TARGET_NAME)\"", + "$(SRCROOT)/$(TARGET_NAME)", ); OTHER_LDFLAGS = ( "-ObjC", @@ -722,15 +775,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; LIBRARY_SEARCH_PATHS = ( "$(inherited)", - "\"$(SRCROOT)/$(TARGET_NAME)\"", - "\"$(SRCROOT)/$(TARGET_NAME)\"", - "\"$(SRCROOT)/$(TARGET_NAME)\"", - "\"$(SRCROOT)/$(TARGET_NAME)\"", - "\"$(SRCROOT)/$(TARGET_NAME)\"", - "\"$(SRCROOT)/$(TARGET_NAME)\"", - "\"$(SRCROOT)/$(TARGET_NAME)\"", - "\"$(SRCROOT)/$(TARGET_NAME)\"", - "\"$(SRCROOT)/$(TARGET_NAME)\"", + "$(SRCROOT)/$(TARGET_NAME)", ); OTHER_LDFLAGS = ( "-ObjC", @@ -745,11 +790,12 @@ isa = XCBuildConfiguration; baseConfigurationReference = 6844E1A0CA2D7D32349444EC /* Pods-celo.debug.xcconfig */; buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = celo/celo.entitlements; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 14; DEAD_CODE_STRIPPING = NO; DEVELOPMENT_TEAM = HDPUB8C3KG; HEADER_SEARCH_PATHS = "$(inherited)"; @@ -777,11 +823,12 @@ isa = XCBuildConfiguration; baseConfigurationReference = A11A894B3E86661FA132FC18 /* Pods-celo.release.xcconfig */; buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = celo/celo.entitlements; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 14; DEVELOPMENT_TEAM = HDPUB8C3KG; HEADER_SEARCH_PATHS = "$(inherited)"; INFOPLIST_FILE = celo/Info.plist; @@ -847,15 +894,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; LIBRARY_SEARCH_PATHS = ( "$(inherited)", - "\"$(SRCROOT)/$(TARGET_NAME)\"", - "\"$(SRCROOT)/$(TARGET_NAME)\"", - "\"$(SRCROOT)/$(TARGET_NAME)\"", - "\"$(SRCROOT)/$(TARGET_NAME)\"", - "\"$(SRCROOT)/$(TARGET_NAME)\"", - "\"$(SRCROOT)/$(TARGET_NAME)\"", - "\"$(SRCROOT)/$(TARGET_NAME)\"", - "\"$(SRCROOT)/$(TARGET_NAME)\"", - "\"$(SRCROOT)/$(TARGET_NAME)\"", + "$(SRCROOT)/$(TARGET_NAME)", ); OTHER_LDFLAGS = ( "-ObjC", @@ -913,15 +952,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; LIBRARY_SEARCH_PATHS = ( "$(inherited)", - "\"$(SRCROOT)/$(TARGET_NAME)\"", - "\"$(SRCROOT)/$(TARGET_NAME)\"", - "\"$(SRCROOT)/$(TARGET_NAME)\"", - "\"$(SRCROOT)/$(TARGET_NAME)\"", - "\"$(SRCROOT)/$(TARGET_NAME)\"", - "\"$(SRCROOT)/$(TARGET_NAME)\"", - "\"$(SRCROOT)/$(TARGET_NAME)\"", - "\"$(SRCROOT)/$(TARGET_NAME)\"", - "\"$(SRCROOT)/$(TARGET_NAME)\"", + "$(SRCROOT)/$(TARGET_NAME)", ); OTHER_LDFLAGS = ( "-ObjC", @@ -950,15 +981,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; LIBRARY_SEARCH_PATHS = ( "$(inherited)", - "\"$(SRCROOT)/$(TARGET_NAME)\"", - "\"$(SRCROOT)/$(TARGET_NAME)\"", - "\"$(SRCROOT)/$(TARGET_NAME)\"", - "\"$(SRCROOT)/$(TARGET_NAME)\"", - "\"$(SRCROOT)/$(TARGET_NAME)\"", - "\"$(SRCROOT)/$(TARGET_NAME)\"", - "\"$(SRCROOT)/$(TARGET_NAME)\"", - "\"$(SRCROOT)/$(TARGET_NAME)\"", - "\"$(SRCROOT)/$(TARGET_NAME)\"", + "$(SRCROOT)/$(TARGET_NAME)", ); PRODUCT_BUNDLE_IDENTIFIER = "com.facebook.REACT.celo-tvOSTests"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -983,15 +1006,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; LIBRARY_SEARCH_PATHS = ( "$(inherited)", - "\"$(SRCROOT)/$(TARGET_NAME)\"", - "\"$(SRCROOT)/$(TARGET_NAME)\"", - "\"$(SRCROOT)/$(TARGET_NAME)\"", - "\"$(SRCROOT)/$(TARGET_NAME)\"", - "\"$(SRCROOT)/$(TARGET_NAME)\"", - "\"$(SRCROOT)/$(TARGET_NAME)\"", - "\"$(SRCROOT)/$(TARGET_NAME)\"", - "\"$(SRCROOT)/$(TARGET_NAME)\"", - "\"$(SRCROOT)/$(TARGET_NAME)\"", + "$(SRCROOT)/$(TARGET_NAME)", ); PRODUCT_BUNDLE_IDENTIFIER = "com.facebook.REACT.celo-tvOSTests"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1037,7 +1052,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.4; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -1074,7 +1089,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.4; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; VALIDATE_PRODUCT = YES; diff --git a/packages/mobile/ios/celo/AppDelegate.m b/packages/mobile/ios/celo/AppDelegate.m index d1c06486b66..6e04239cbce 100644 --- a/packages/mobile/ios/celo/AppDelegate.m +++ b/packages/mobile/ios/celo/AppDelegate.m @@ -13,20 +13,23 @@ #import #import -#if __has_include() -#import // This is used for versions of react >= 0.40 -#else -#import "RNSentry.h" // This is used for versions of react < 0.40 -#endif - @import Firebase; #import "RNFirebaseNotifications.h" #import "RNFirebaseMessaging.h" +#import "RNSplashScreen.h" + +// Use same key as react-native-secure-key-store +// so we don't reset already working installs +static NSString * const kHasRunBeforeKey = @"RnSksIsAppInstalled"; @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + // Reset keychain on first run to clear existing Firebase credentials + // Note: react-native-secure-key-store also does that but is run too late + // and hence can't clear Firebase credentials + [self resetKeychainIfNecessary]; [FIRApp configure]; [RNFirebaseNotifications configure]; RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions]; @@ -34,8 +37,7 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:( moduleName:@"celo" initialProperties:nil]; - [RNSentry installWithRootView:rootView]; - + [RNSplashScreen showSplash:@"LaunchScreen" inRootView:rootView]; rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1]; self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; @@ -68,4 +70,27 @@ - (void)application:(UIApplication *)application didRegisterUserNotificationSett [[RNFirebaseMessaging instance] didRegisterUserNotificationSettings:notificationSettings]; } +// Reset keychain on first app run, this is so we don't run with leftover items +// after reinstalling the app +- (void)resetKeychainIfNecessary +{ + NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; + if ([defaults boolForKey:kHasRunBeforeKey]) { + return; + } + + NSArray *secItemClasses = @[(__bridge id)kSecClassGenericPassword, + (__bridge id)kSecAttrGeneric, + (__bridge id)kSecAttrAccount, + (__bridge id)kSecClassKey, + (__bridge id)kSecAttrService]; + for (id secItemClass in secItemClasses) { + NSDictionary *spec = @{(__bridge id)kSecClass:secItemClass}; + SecItemDelete((__bridge CFDictionaryRef)spec); + } + + [defaults setBool:YES forKey:kHasRunBeforeKey]; + [defaults synchronize]; +} + @end diff --git a/packages/mobile/ios/celo/Info.plist b/packages/mobile/ios/celo/Info.plist index b44a9fa7dca..8ce66d27d44 100644 --- a/packages/mobile/ios/celo/Info.plist +++ b/packages/mobile/ios/celo/Info.plist @@ -17,11 +17,11 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.4.2 + 1.5.1 CFBundleSignature ???? CFBundleVersion - 7 + 14 LSRequiresIPhoneOS NSAppTransportSecurity @@ -39,10 +39,6 @@ This is so you can scan QR codes. NSContactsUsageDescription This is so you can invite and transact with them. - NSLocationAlwaysUsageDescription - N/A - NSLocationWhenInUseUsageDescription - N/A UIAppFonts Hind-Bold.ttf @@ -67,5 +63,7 @@ UIViewControllerBasedStatusBarAppearance + ITSAppUsesNonExemptEncryption + diff --git a/packages/mobile/ios/celoTests/Info.plist b/packages/mobile/ios/celoTests/Info.plist index 298de9f8779..fb277156263 100644 --- a/packages/mobile/ios/celoTests/Info.plist +++ b/packages/mobile/ios/celoTests/Info.plist @@ -15,10 +15,10 @@ CFBundlePackageType BNDL CFBundleShortVersionString - 1.4.2 + 1.5.1 CFBundleSignature ???? CFBundleVersion - 7 + 14 diff --git a/packages/mobile/jest.config.js b/packages/mobile/jest.config.js index fb64d4e99b0..01797f43d27 100644 --- a/packages/mobile/jest.config.js +++ b/packages/mobile/jest.config.js @@ -1,18 +1,19 @@ -const defaultConfig = require('../../jest.config.js') const reactNativeJestPreset = require('react-native/jest-preset') +const { defaults: tsjPreset } = require('ts-jest/presets') module.exports = { - ...defaultConfig, + ...tsjPreset, globals: { navigator: true, 'ts-jest': { + babelConfig: true, // Disables type-check when running tests as it takes valuable time // and is redundant with the tsc build step isolatedModules: true, + tsConfig: 'tsconfig.test.json', }, window: true, }, - // Override default platform to android for now haste: { ...reactNativeJestPreset.haste, defaultPlatform: 'android', @@ -25,8 +26,12 @@ module.exports = { modulePathIgnorePatterns: ['/node_modules/(.*)/node_modules/react-native'], preset: 'react-native', setupFilesAfterEnv: ['/jest_setup.ts'], - snapshotSerializers: ['enzyme-to-json/serializer'], + testPathIgnorePatterns: ['/node_modules/', '/e2e'], + transform: { + ...tsjPreset.transform, + '\\.js$': '/../../node_modules/react-native/jest/preprocessor.js', + }, transformIgnorePatterns: [ - 'node_modules/(?!(@celo/)?react-native|@react-navigation|redux-persist|date-fns)', + 'node_modules/(?!(@celo/)?react-native|@react-navigation|@react-native-community|react-navigation|redux-persist|date-fns)', ], } diff --git a/packages/mobile/locales/en-US/accountScreen10.json b/packages/mobile/locales/en-US/accountScreen10.json index 37a2a933e0b..1e0faa1839d 100644 --- a/packages/mobile/locales/en-US/accountScreen10.json +++ b/packages/mobile/locales/en-US/accountScreen10.json @@ -13,8 +13,8 @@ "enableCeloLite": "Enable Celo Lite", "celoLiteDetail": "Celo Lite mode allows you to communicate with the Celo Network through a trusted node. You can always change this mode in app settings.", - "testFaqHere": "<0>Test FAQ is <1>here", - "termsOfServiceHere": "<0>Terms of service are <1>here", + "testFaqLink": "Celo Wallet FAQ", + "termsOfServiceLink": "Terms of service", "editProfile": "Edit Profile", "cancel": "Cancel", "changeProfilePhoto": "Change Profile Photo", diff --git a/packages/mobile/locales/en-US/global.json b/packages/mobile/locales/en-US/global.json index 1f8680a22e1..204ccb7b440 100644 --- a/packages/mobile/locales/en-US/global.json +++ b/packages/mobile/locales/en-US/global.json @@ -65,6 +65,7 @@ "verifyFailed": "Failed to verify", "canNotRequestFromUnverified": "Can not request from unverified users", "restartApp": "Restart App", + "quitApp": "Quit", "loading": "Loading…", "inviteFailed": "Failure sending invite", "importContactsFailed": "Failed to import contacts", @@ -91,6 +92,7 @@ "web3FailedToSync": "Failing to sync, check your network connection", "errorDuringSync": "Error occurred during sync, please try again later", "calculateFeeFailed": "Could not calculate fee", + "failedToSwitchSyncModes": "Failed to switch sync modes", "gold": "Gold", "localCurrencyTitle": "Select Currency" } diff --git a/packages/mobile/locales/en-US/inviteFlow11.json b/packages/mobile/locales/en-US/inviteFlow11.json index 21557acd44c..c191488a482 100644 --- a/packages/mobile/locales/en-US/inviteFlow11.json +++ b/packages/mobile/locales/en-US/inviteFlow11.json @@ -15,6 +15,7 @@ "redeemSuccess": "Successfully redeemed invite code.", "redeemFailed": "Failure redeeming code ", "redeemInviteTimeout": "Redeem Invite Timed out", + "accountSetupFailed": "Failed to create your wallet", "emptyInviteCode": "Invite code has no funds", "withdrawGoldFromEscrow": "Withdrawing gold from escrow...", "transferGoldToAccount": "Gold transferred to your account!", diff --git a/packages/mobile/locales/en-US/nuxNamePin1.json b/packages/mobile/locales/en-US/nuxNamePin1.json index 55478d39a4d..d6e699559d4 100644 --- a/packages/mobile/locales/en-US/nuxNamePin1.json +++ b/packages/mobile/locales/en-US/nuxNamePin1.json @@ -38,8 +38,8 @@ "validating": "Great! Validating copied invite code", "inviteAccepted": "🎉 Invite Accepted!", "askForInvite": { - "0": "Request an invite from someone with Celo Wallet or sign up for the Testnet at ", - "1": "celo.org/build/wallet" + "0": "Request an invite from a friend on Celo or sign up for the {{testnet}} network at ", + "1": "celo.org/app" } }, "enterFullName": "Please enter your first and last name", diff --git a/packages/mobile/locales/en-US/nuxVerification2.json b/packages/mobile/locales/en-US/nuxVerification2.json index 8dd4f5b7ffa..0d30fc4237a 100644 --- a/packages/mobile/locales/en-US/nuxVerification2.json +++ b/packages/mobile/locales/en-US/nuxVerification2.json @@ -13,6 +13,7 @@ "country": "Country", "phoneNumber": "Phone Number", "invalidPhone": "Invalid Phone Number", + "missingFullName": "Please enter full name", "allowSmsPermissions": "Allow Celo Wallet to send and view SMS messages", "dontAsk": "Don't ask again", "deny": "Deny", @@ -54,5 +55,14 @@ "errorRedeemingCode": "Failed to redeem code.", "pleaseRetry": "Please retry verification.", "retryVerification": "Retry Verification", - "codeAccepted": "Accepted" + "codeAccepted": "Accepted", + "education": { + "header": "Verify Your Phone", + "body1": "Next, please verify your phone number.", + "body2": + "Verifying makes it easier to send and receive with friends. You can also skip this step and return to it later.", + "learnMore": "Learn more about phone verification", + "start": "Start Verification", + "skip": "Skip For Now" + } } diff --git a/packages/mobile/locales/en-US/sendFlow7.json b/packages/mobile/locales/en-US/sendFlow7.json index 3ed8c4b8b80..58e9f780b83 100644 --- a/packages/mobile/locales/en-US/sendFlow7.json +++ b/packages/mobile/locales/en-US/sendFlow7.json @@ -45,6 +45,7 @@ "sentPayment": "Sent Payment", "mobileNumber": "Mobile #", "walletAddress": "Wallet Address", + "unknown": "Unknown", "inviteSMS": "Hi{{name}}! I would like to invite you to join the Celo payments network. Your invite code is: {{code}} You can install the Celo application from the following link: {{link}}", "inviteSent": "Invite code sent!", diff --git a/packages/mobile/locales/en-US/walletFlow5.json b/packages/mobile/locales/en-US/walletFlow5.json index c38be51476d..bccc986f7d3 100644 --- a/packages/mobile/locales/en-US/walletFlow5.json +++ b/packages/mobile/locales/en-US/walletFlow5.json @@ -59,8 +59,9 @@ "escrowedPaymentReminderSms": "A friendly reminder that you haven't yet redeemed your Celo Dollars!", "testnetAlert": { - "0": "Testnet", - "1": "A friendly reminder you're using a Testnet build - the balances here are not real." + "0": "{{testnet}}", + "1": + "A friendly reminder you're using the {{testnet}} network build - the balances are not real" }, "dismiss": "Dismiss", "localCurrencyEqual": "Equal to <2>{{localValue}} {{localCurrencyCode}}", diff --git a/packages/mobile/locales/en.json b/packages/mobile/locales/en.json deleted file mode 100644 index 52971d73eb3..00000000000 --- a/packages/mobile/locales/en.json +++ /dev/null @@ -1,103 +0,0 @@ -{ - "Choose Language": "Choose Language", - "syncText": { - "0": "Celo keeps your assets secure and enables you to send and receive value with anyone", - "1": "{{CeloDollars}} are a stable digital asset that tracks to the value of the US {{Dollar}}", - "2": "Verify your phone number so other Celo users can find you and send you value" - }, - "progressBarText": "Syncing with Network", - "Welcome to Celo": "Welcome to Celo", - "inviteText": { - "0": - "Enter your invitation code. If you do not have one, ask someone in the Celo Community to invite you.", - "1": - "By joining this application, you agree to share your name & phone number with us. Learn more at ", - "2": "celo.org" - }, - "namePrompt": "Please enter your first and last name", - "importPrompt": { - "0": "Already have a Celo wallet? ", - "1": "Import it" - }, - "activity": "Activity", - "Full Name": "Full Name", - "Invitation Code": "Invitation Code", - "Opt in": "Opt in", - "Continue": "Continue", - "available": "Available", - "cancel": "Cancel", - "continue": "Continue", - "edit": "Edit", - "exchange": "Exchange", - "newBalance": "New Balance", - "send": "Send", - "payment": "Payment", - "skip": "Skip", - "submit": "Submit", - "wallet": "Wallet", - "backupKey": "Backup Phrase", - "backupIntro": { - "0": - "If you lose your phone or delete the Celo app, you will lose all of the gold and {{dollars}} in your wallet.", - "1": - "You can back up your wallet by writing down a backup phrase on a piece of paper and storing it securely. This will allow you to restore your wallet in the future if you need to.", - "2": - "Don't take a screenshot or save on your phone notes. Make sure to write the backup phrase down and keep it safe." - }, - "areYouSure?": "Are you sure?", - "backupSkipText": { - "0": "Without a Backup Phrase, you can lose access to your wallet ", - "1": "forever" - }, - "shareBackupText": "Share your Backup Phrase with one other person who you completely trust.", - "securityTips": { - "title": "Security Tips", - "text": { - "0": "Write this phrase down on a piece of paper and keep it somewhere safe. ", - "1": "Don’t show your phrase to anyone else. They can access your wallet if they see it." - } - }, - "Send with Whatsapp": "Send with Whatsapp", - "whatsappMessage": - "Important: please keep this private. \n\nI'm sending you the Backup Phrase to my Celo Wallet: ", - "setBackupInfo": - "Once you have finished, we will verify that you wrote down the Backup Phrase correctly.", - "Question": "Question", - "questionPhrase": { - "0": "What is the ", - "1": " word of your Backup Phrase?" - }, - "Don't Know?": "Don't Know?", - "Return to backup phrase": "Return to backup phrase", - "Try Again": "Try Again", - "wrongAnswerText": - "We will take you back to the screen with your backup phrase so that you ensure you wrote it down correctly.", - "See Backup Key": "See Backup Phrase", - "Backup Key Set": "Backup Phrase Set", - "backupCompleteText": - "Please do not lose this phrase. It is critical that you maintain this in a safe place, as this is the only way to unlock your wallet should you lose your phone.", - "Done": "Done", - "pending": "Pending", - "date_at_time": "at", - "edit_profile": "Edit Profile", - "backup_key": "Backup Phrase", - "invite": "Invite", - "celo_rewards": "Celo Rewards", - "language_settings": "Language Settings", - "edit_name": "Edit Name", - "exchanges": { - "includesFee": "*includes Exchange Fee", - "rate": "Exchange Rate", - "review": "Review Exchange", - "fee": "Fee" - }, - "faq_terms_footer": "<0>Test FAQ is <1>here<2> Terms of service are <3>here", - "invalidInvitation": "Invalid Invitation Code ", - "incorrectPin": "Incorrect PIN", - "photosNUX": { - "0": "Celo uses your address book to show photos", - "1": "You can change a photo by updating your contacts", - "2": "Only you can see these photos locally on your phone" - }, - "backToWallet": "Back to Wallet" -} diff --git a/packages/mobile/locales/es-419/accountScreen10.json b/packages/mobile/locales/es-419/accountScreen10.json index b0c62240532..785c8454e71 100755 --- a/packages/mobile/locales/es-419/accountScreen10.json +++ b/packages/mobile/locales/es-419/accountScreen10.json @@ -13,8 +13,8 @@ "enableCeloLite": "Habilitar Celo Lite", "celoLiteDetail": "El modo Celo Lite te permite comunicarte con la Red Celo a través de un nodo confiable. Puedes cambiar este modo en la configuración de la aplicación.", - "testFaqHere": "<1>Aquí<0> están las preguntas frecuentes de la prueba. ", - "termsOfServiceHere": "<1>Aquí<0> están las Condiciones de servicio.", + "testFaqLink": "Las Preguntas Frecuentes del Monedero Celo", + "termsOfServiceLink": "Las Condiciones de Servicio", "editProfile": "Editar perfil", "cancel": "Cancelar", "changeProfilePhoto": "Cambiar la foto de perfil", diff --git a/packages/mobile/locales/es-419/backupKeyFlow6.json b/packages/mobile/locales/es-419/backupKeyFlow6.json index 26763e646e1..08b0fc7e2f5 100755 --- a/packages/mobile/locales/es-419/backupKeyFlow6.json +++ b/packages/mobile/locales/es-419/backupKeyFlow6.json @@ -1,71 +1,72 @@ { - "getBackupKey": "Get Backup Key", - "getYourKey": "Get Your Key", - "viewBackupKey": "View Backup Key", - "setUpSocialBackup": "Set Up Safeguards", - "viewSafeguards": "View Safeguards", - "failedFetchMnemonic": "Failed to fetch your Backup Key", - "backupAndRecovery": "Backup and Recovery", - "backupKey": "Backup Key", - "yourBackupKey": "Your Backup Key", - "delayBackup": "Dismiss for an hour", - "backupKeyNotification": "Without a Backup Key, you may lose access to your wallet.", + "getBackupKey": "Obtener Clave de Respaldo", + "getYourKey": "Obtenga su clave", + "viewBackupKey": "Ver Clave de Respaldo", + "setUpSocialBackup": "Configurar Salvaguardas", + "viewSafeguards": "Ver Salvaguardas", + "failedFetchMnemonic": "No se pudo obtener su clave de respaldo", + "backupAndRecovery": "Respaldo y recuperación", + "backupKey": "Clave de respaldo", + "yourBackupKey": "Su clave de respaldo", + "delayBackup": "Descartar por una hora", + "backupKeyNotification": "Sin una clave de respaldo, puede perder el acceso a la billetera.", "backupKeyIntro": { "0": - "Your Backup Key is the one and only key to your Celo Wallet. With this key, you can access your funds anytime, anywhere.", - "1": "KEEP THIS KEY SAFE AND PRIVATE.", - "2": "Congrats, you’ve sucessfully retrieved your Backup Key! A reminder, ", - "3": "KEEP THIS KEY SAFE.", - "4": "For more security set up Safeguards for your wallet.", + "Su clave de respaldo es la única clave para la billetera Celo. Con esta clave, puede acceder a sus fondos en cualquier momento y en cualquier lugar.", + "1": "MANTENGA LA SEGURIDAD Y PRIVACIDAD DE ESTA CLAVE.", + "2": "¡Felicitaciones! Ha recuperado correctamente su clave de respaldo. Un recordatorio, ", + "3": "MANTENGA LA SEGURIDAD DE ESTA CLAVE.", + "4": "Para mayor seguridad, configure las Salvaguardas de su billetera.", "5": - "You’ve also set up Safeguards! You’ll be able to restore your account with the help of the two friends. ", - "6": "KEEP YOUR SAFEGUARDS SECRET" + "¡También ha configurado las salvaguardas! Podrá restaurar su cuenta con la ayuda de dos amigos. ", + "6": "MANTENGA EN SECRETO SUS SALVAGUARDAS" }, "backupSkipText": { - "0": "Without a Backup Key, you can lose access to your wallet ", - "1": "forever." + "0": "Sin una clave de respaldo, puede perder el acceso a la billetera ", + "1": "para siempre." }, - "backupRecovery": "Share your Backup Key with only one other person who you completely trust.", - "securityTip": "If you lose your backup key, you will lose access to your Celo Dollars and Gold.", + "backupRecovery": + "Comparta la clave de respaldo con una sola persona en la que confíe plenamente.", + "securityTip": "Si pierde su clave de respaldo, perderá el acceso a sus dólares y oro Celo.", "backupKeySummary": - "Please write down your Backup Key. If your phone is stolen or lost, you will need the Backup Key to access your Celo Wallet.", + "Escriba su clave de respaldo. Si se pierde o le roban su teléfono, necesitará su clave de respaldo para acceder a la billetera Celo.", "bothBackupsDone": { - "0": "Congratulations!", + "0": "¡Felicitaciones!", "1": - "You're all done! If you would like to review your recovery secrets, you can always return here later." + "¡Listo! Si desea revisar sus secretos para recuperación, siempre puede volver aquí más tarde." }, "dontLoseIt": - "Please do not lose this key. It is critical that you maintain this in a safe place, as this is the only way to unlock your wallet should you lose your phone.", + "No pierda esta clave. Es de suma importancia que la mantenga en un lugar seguro, ya que es la única forma de desbloquear su billetera si pierde su celular.", "backupPrompt": - "For the security of your funds, your account is frozen until you get your Backup Key", - "copied": "Key copied to clipboard", - "savedConfirmation": "I have saved my Backup Key.", - "confirmBackupKey": "Confirm Your Backup Key", + "Para seguridad de sus fondos, su cuenta está bloqueada hasta que obtenga su clave de respaldo.", + "copied": "Clave copiada al portapapeles", + "savedConfirmation": "He guardado mi clave de respaldo", + "confirmBackupKey": "Confirme su clave de respaldo", "backupQuizInfo": - "Please verify your Backup Key by selecting the words below in the correct order.", - "backupQuizWordCount": "Word {{index}} of {{total}}", - "invalidBackupPhrase": "Invalid Backup Key", - "importBackupFailed": "Importing Wallet Failed", - "backupQuizFailed": "Incorrect Backup Key, please try again", + "Verifique su clave de respaldo seleccionando las palabras que aparecen debajo en el orden correcto.", + "backupQuizWordCount": "Palabra {{index}} de {{total}}", + "invalidBackupPhrase": "Clave de respaldo inválida", + "importBackupFailed": "No se pudo importar la billetera", + "backupQuizFailed": "Clave de respaldo incorrecta; vuelva a intentarlo", "backupComplete": { - "0": "Success!", - "1": "Next, you can set up Safeguards.", - "2": "You’re all set!" + "0": "¡Correcto!", + "1": "Ahora puede configurar las salvaguardas.", + "2": "¡Está todo configurado!" }, "socialBackupIntro": { - "header": "Introducing Safeguards", + "header": "Cómo incluir salvaguardas", "body": - "Safeguards is an additional layer of protection for your account in case you lose your Backup Key. Once set up, you’ll be able to restore your account with the help of two friends or family members. ", - "warning": "NEVER TELL ANYONE YOUR SAFEGUARDS’ IDENTITIES.", - "skip": "Skip For Now" + "Las salvaguardas son una capa adicional de resguardo para su cuenta en el caso de que pierda la clave de respaldo. Una vez configuradas las salvaguardas, podrá restaurar su cuenta con la ayuda de dos amigos o miembros de su familia. ", + "warning": "NO DEBE REVELARLE A NADIE LAS IDENTIDADES DE SUS SALVAGUARDAS.", + "skip": "Omitir por ahora" }, - "socialBackupPhraseHeader": "Safeguard Phrase {{index}}", + "socialBackupPhraseHeader": "Frase de protección {{index}}", "socialBackup": { "body": - "Share each phrase below with a friend. Be sure to send only one phrase to each person.", - "confirmation": "I have sent each Safeguard phrase to a trusted friend.", - "yourSafeguards": "Your Safeguards" + "Comparta cada frase que aparece a continuación con un amigo. Asegúrese de enviar solo una frase a cada persona.", + "confirmation": "He enviado cada una de las salvaguardas a un amigo de confianza.", + "yourSafeguards": "Sus salvaguardas" }, "backupPhrasePlaceholder": - "horse leopard dog monkey shark tiger lemur whale squid wolf squirrel mouse lion elephant cat shrimp bear penguin deer turtle fox zebra goat giraffe" + "caballo leopardo perro mono tiburón tigre lémur ballena calamar lobo ardilla ratón león elefante gato langostino oso pingüino ciervo tortuga zorro cebra cabra jirafa" } diff --git a/packages/mobile/locales/es-419/global.json b/packages/mobile/locales/es-419/global.json index 0076dc4d25f..438e970a108 100755 --- a/packages/mobile/locales/es-419/global.json +++ b/packages/mobile/locales/es-419/global.json @@ -65,6 +65,7 @@ "verifyFailed": "Falla durante la verificación", "canNotRequestFromUnverified": "No se puede solicitar a usuarios no verificados.", "restartApp": "Reiniciar la aplicación", + "quitApp": "Dejar", "loading": "Cargando…", "inviteFailed": "Falló el envío de la invitación", "importContactsFailed": "Error al importar contactos", @@ -92,6 +93,7 @@ "web3FailedToSync": "Fallo la sincronización, por favor verifica tu conexión", "errorDuringSync": "Ocurrió un error duranet la sincronización, por favor intente más tarde", "calculateFeeFailed": "No se pudo calcular la comisión", + "failedToSwitchSyncModes": "Error al cambiar de red modos", "gold": "Oro", "localCurrencyTitle": "Seleccione el tipo de moneda" } diff --git a/packages/mobile/locales/es-419/inviteFlow11.json b/packages/mobile/locales/es-419/inviteFlow11.json index 6c3c409f752..8d8889a8c7a 100755 --- a/packages/mobile/locales/es-419/inviteFlow11.json +++ b/packages/mobile/locales/es-419/inviteFlow11.json @@ -15,6 +15,7 @@ "redeemSuccess": "Código de invitación validado de forma exitosa.", "redeemFailed": "No pudimos validar el código de invitación", "redeemInviteTimeout": "Canjear invitación agotada", + "accountSetupFailed": "No se pudo crear tu monedero", "emptyInviteCode": "El código de invitación no tiene fondos", "withdrawGoldFromEscrow": "Retirarndo el oro de la cuenta...", "transferGoldToAccount": "Oro transferido a tu cuenta!", diff --git a/packages/mobile/locales/es-419/nuxNamePin1.json b/packages/mobile/locales/es-419/nuxNamePin1.json index 31ce3c2196a..5e5149443a9 100755 --- a/packages/mobile/locales/es-419/nuxNamePin1.json +++ b/packages/mobile/locales/es-419/nuxNamePin1.json @@ -39,8 +39,8 @@ "validating": "Genial! Validando el código de invitación copiado", "inviteAccepted": "🎉 Invitación aceptada!", "askForInvite": { - "0": "Solicite una invitación de alguien con Celo Monedero o regístrese en Testnet en ", - "1": "celo.org/build/wallet" + "0": "Solicite una invitación de un amigo en Celo o regístrese en la red {{testnet}} en ", + "1": "celo.org/app" } }, "enterFullName": "Ingresa tu nombre y apellido", diff --git a/packages/mobile/locales/es-419/nuxRestoreWallet3.json b/packages/mobile/locales/es-419/nuxRestoreWallet3.json index 60566eaddb5..2bb58ffdac4 100755 --- a/packages/mobile/locales/es-419/nuxRestoreWallet3.json +++ b/packages/mobile/locales/es-419/nuxRestoreWallet3.json @@ -1,18 +1,18 @@ { - "title": "Restaurar tu Monedero Celo", + "title": "Restaurar el Monedero Celo", "userYourBackupKey": - "¿Ya tiene un Monedero Celo? Ingrese su Clave de Respaldo para restaurarla a este teléfono.", + "¿Ya tiene un Monedero Celo? Ingrese su Clave de Respaldo para restaurarla en este teléfono.", "backupKeyPrompt": - "caballo leopardo perro mono tiburón tigre lémur ballena calamar lobo ardilla ratón león elefante gato camarones oso pingüino...", - "tip": "Consejo: ", - "backupKeyTip": "Las Claves de Respaldo son de 24 palabras, separadas por un espacio.", + "caballo leopardo perro mono tiburón tigre lémur ballena calamar lobo ardilla ratón león elefante gato langostino oso pingüino ciervo tortuga zorro cebra cabra jirafa", + "tip": "Sugerencia: ", + "backupKeyTip": "Las Claves de Respaldo tienen 24 palabras, separadas por un espacio.", "restoreWallet": "Restaurar", - "emptyWalletWarning": "Este Monedero esta vacio", - "useEmptyAnyway": "¿Te gustaría usarlo de todos modos?", - "useEmptyWallet": "Usar Monedero Vacío", - "tryAnotherKey": "Probar con otra Clave de Respaldo", - "restoreSocial": "Restore with Safeguards", + "emptyWalletWarning": "Esta billetera está vacía.", + "useEmptyAnyway": "¿Le gustaría usarla de todas maneras?", + "useEmptyWallet": "Usar la billetera vacía", + "tryAnotherKey": "Pruebe otra Clave de Respaldo", + "restoreSocial": "Restaurar con Salvaguardas", "socialImportInfo": - "If you enabled Safeguards, you sent Safeguard Phrases to two friends. Please retrieve these phrases and enter them here (in any order) to regain access to your funds.", - "socialTip": "Phrases are lists of 13 words separated by spaces." + "Si activó las Salvaguardas, envió frases de protección a dos amigos. Recupere estas frases e ingréselas aquí (en cualquier orden) para volver a tener acceso a sus fondos.", + "socialTip": "Las frases son listas de 13 palabras separadas por espacios." } diff --git a/packages/mobile/locales/es-419/nuxVerification2.json b/packages/mobile/locales/es-419/nuxVerification2.json index b2eaf165799..71c3536d285 100755 --- a/packages/mobile/locales/es-419/nuxVerification2.json +++ b/packages/mobile/locales/es-419/nuxVerification2.json @@ -14,6 +14,7 @@ "country": "País", "phoneNumber": "Número de teléfono", "invalidPhone": "Número de teléfono inválido", + "missingFullName": "Por favor ingresa tu nombre completo", "allowSmsPermissions": "Permitir que el Monedero Celo envíe y vea mensajes de texto (SMS)", "dontAsk": "No volver a preguntar", "deny": "Denegar", @@ -55,5 +56,14 @@ "errorRedeemingCode": "Error al canjear el código.", "pleaseRetry": "Por favor reinicie la verificación.", "retryVerification": "Reintentar la verificación", - "codeAccepted": "Aceptado" + "codeAccepted": "Aceptado", + "education": { + "header": "~~Verify Your Phone", + "body1": "~~Next, please verify your phone number.", + "body2": + "~~Verifying makes it easier to send and receive with friends. You can also skip this step and return to it later.", + "learnMore": "~~Learn more about phone verification", + "start": "~~Start Verification", + "skip": "~~Skip For Now" + } } diff --git a/packages/mobile/locales/es-419/sendFlow7.json b/packages/mobile/locales/es-419/sendFlow7.json index 3981f806ff9..0e2c1ea21d5 100755 --- a/packages/mobile/locales/es-419/sendFlow7.json +++ b/packages/mobile/locales/es-419/sendFlow7.json @@ -45,6 +45,7 @@ "sentPayment": "Enviar pago", "mobileNumber": "Número de teléfono", "walletAddress": "Dirección del Monedero", + "unknown": "Desconocido", "inviteSMS": "Hola{{name}}! Me gustaría invitarte a que te unas a la red de pagos de Celo. Tu código de invitación es: {{code}} Podés instalarte la aplicación Celo desde el siguiente vínculo: {{link}}", "inviteSent": "Código de invitación enviado!", diff --git a/packages/mobile/locales/es-419/walletFlow5.json b/packages/mobile/locales/es-419/walletFlow5.json index c09de057af6..26585488898 100755 --- a/packages/mobile/locales/es-419/walletFlow5.json +++ b/packages/mobile/locales/es-419/walletFlow5.json @@ -60,9 +60,9 @@ "escrowedPaymentReminderSms": "¡Un recordatorio amistoso de que aún no ha canjeado sus dólares de celo!", "testnetAlert": { - "0": "Testnet", + "0": "{{testnet}}", "1": - "Un recordatorio amistoso de que está utilizando una compilación de Testnet - los saldos aquí no son reales." + "Un recordatorio amistoso de que está utilizando la compilación de red {{testnet}} - los saldos aquí no son reales." }, "dismiss": "Ocultar", "localCurrencyEqual": "Igual a <2>{{localValue}} {{localCurrencyCode}}", diff --git a/packages/mobile/locales/es.json b/packages/mobile/locales/es.json deleted file mode 100644 index e6c1894f262..00000000000 --- a/packages/mobile/locales/es.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "wallet": "Monedero", - "send": "Enviar", - "payment": "Pago", - "exchange": "Intercambiar", - "pending": "Pendiente", - "date_at_time": "a las" -} diff --git a/packages/mobile/rn-cli.config.js b/packages/mobile/metro.config.js similarity index 69% rename from packages/mobile/rn-cli.config.js rename to packages/mobile/metro.config.js index f17315d3837..56d8e6e58ab 100644 --- a/packages/mobile/rn-cli.config.js +++ b/packages/mobile/metro.config.js @@ -7,13 +7,12 @@ const isE2E = process.env.CELO_TEST_CONFIG === 'e2e' const cwd = path.resolve(__dirname) const root = path.resolve(cwd, '../..') const escapedRoot = escapeStringRegexp(root) -const rnRegex = new RegExp(`${escapedRoot}\/node_modules\/(react-native)\/.*`) const celoRegex = new RegExp( - `${escapedRoot}\/packages\/(?!mobile|utils|walletkit|react-components).*` + `${escapedRoot}\/packages\/(?!mobile|utils|walletkit|contractkit|react-components).*` ) const nestedRnRegex = new RegExp(`.*\/node_modules\/.*\/node_modules\/(react-native)\/.*`) const componentsRnRegex = new RegExp(`.*react-components\/node_modules\/(react-native)\/.*`) -const blist = [rnRegex, celoRegex, nestedRnRegex, componentsRnRegex] +const blist = [celoRegex, nestedRnRegex, componentsRnRegex] const defaultSourceExts = require('metro-config/src/defaults/defaults').sourceExts module.exports = { @@ -24,16 +23,12 @@ module.exports = { extraNodeModules: { ...nodeLibs, 'crypto-js': path.resolve(cwd, 'node_modules/crypto-js'), + fs: require.resolve('react-native-fs'), 'isomorphic-fetch': require.resolve('cross-fetch'), net: require.resolve('react-native-tcp'), - 'react-native': path.resolve(cwd, 'node_modules/react-native'), - 'react-native-fs': path.resolve(cwd, 'node_modules/react-native-fs'), - 'react-native-screens': path.resolve(cwd, 'node_modules/react-native-screens'), - 'react-native-svg': path.resolve(cwd, 'node_modules/react-native-svg'), vm: require.resolve('vm-browserify'), }, sourceExts: isE2E ? ['e2e.ts', 'e2e.js'].concat(defaultSourceExts) : defaultSourceExts, }, - watchFolders: [root], } diff --git a/packages/mobile/package.json b/packages/mobile/package.json index 2f6f923c6d7..19e912a23de 100644 --- a/packages/mobile/package.json +++ b/packages/mobile/package.json @@ -1,14 +1,12 @@ { "name": "@celo/mobile", - "version": "1.5.0", + "version": "1.5.1", "author": "Celo", "license": "Apache-2.0", "private": true, "scripts": { "start": "react-native start", "start:bg": "react-native start &", - "start:android": "react-native run-android", - "start:ios": "react-native run-ios", "lint": "tslint -c tslint.json --project tsconfig.json", "build": "yarn run build:ts && yarn run build:metro", "build:sdk": "yarn --cwd ../walletkit build:for-env", @@ -16,7 +14,8 @@ "build:metro": "echo 'NOT WORKING RIGHT NOW'", "build:gen-graphql-types": "gql-gen --schema http://localhost:8080/graphql --template graphql-codegen-typescript-template --out ./typings/ 'src/**/*.tsx'", "predev": "./scripts/pre-dev.sh", - "dev": "react-native run-android --appIdSuffix \"debug\"", + "dev": "react-native run-android --appIdSuffix \"debug\" --no-packager && yarn start || echo 'Could not start metro server'", + "dev:ios": "react-native run-ios --simulator \"iPhone 11\"", "dev:show-menu": "adb devices | grep '\t' | awk '{print $1}' | sed 's/\\s//g' | xargs -I {} adb -s {} shell input keyevent 82", "dev:clear-data": "adb shell pm clear org.celo.mobile.debug", "dev:clean-android": "cd android && ./gradlew clean", @@ -34,12 +33,9 @@ "test:dry-run-e2e": "yarn dev:emulator && cd android && yarn test:build-e2e && yarn test:run-e2e", "test:detox": "CELO_TEST_CONFIG=e2e detox test -c android.emu.debug -a e2e/tmp/ --take-screenshots=failing --record-logs=failing --detectOpenHandles -l verbose", "test:unlock": "./scripts/unlock.sh", + "test:verify-locales": "./scripts/verify_locales.sh", "pre-deploy": "./scripts/pre-deploy.sh", - "prepare": "patch-package", - "postinstall": "sh scripts/fix_rn_version.sh; patch-package", - "update-disclaimer": "yarn licenses generate-disclaimer > android/app/src/main/assets/custom/LicenseDisclaimer.txt", - "test-licenses": "yarn licenses list --prod | grep '\\(─ GPL\\|─ (GPL-[1-9]\\.[0-9]\\+ OR GPL-[1-9]\\.[0-9]\\+)\\)' && echo 'Found GPL license(s). Use 'yarn licenses list --prod' to look up the offending package' || echo 'No GPL licenses found'", - "verify-locales": "./scripts/verify_locales.sh" + "deploy:update-disclaimer": "yarn licenses generate-disclaimer > android/app/src/main/assets/custom/LicenseDisclaimer.txt" }, "rnpm": { "assets": [ @@ -47,14 +43,17 @@ ] }, "dependencies": { - "@celo/client": "691d471", + "@celo/client": "9575a01", + "@celo/contractkit": "^0.1.6", "@celo/react-components": "1.0.0", - "@celo/react-native-sms-retriever": "git+https://github.com/celo-org/react-native-sms-retriever#d3a2fdb", + "@celo/react-native-sms-retriever": "git+https://github.com/celo-org/react-native-sms-retriever#b88e502", "@celo/utils": "^0.1.1", "@celo/walletkit": "^0.0.14", - "@react-native-community/netinfo": "^2.0.4", - "@segment/analytics-react-native": "^1.1.0-beta.1", - "@segment/analytics-react-native-firebase": "^1.1.0-beta.1", + "@react-native-community/async-storage": "^1.6.2", + "@react-native-community/netinfo": "^4.4.0", + "@segment/analytics-react-native": "^1.1.0-beta.2", + "@segment/analytics-react-native-firebase": "^1.1.0-beta.2", + "@sentry/react-native": "^1.0.7", "@ungap/url-search-params": "^0.1.2", "Base64": "^1.0.1", "apollo-boost": "^0.3.1", @@ -76,56 +75,61 @@ "moment-timezone": "^0.5.23", "node-libs-react-native": "^1.0.3", "numeral": "^2.0.6", - "react": "16.8.3", + "react": "16.9.0", "react-apollo": "^2.4.1", "react-async-hook": "^3.4.0", "react-i18next": "^8.3.8", - "react-native": "0.59.10", - "react-native-android-open-settings": "^1.2.0", + "react-native": "^0.61.2", + "react-native-android-open-settings": "^1.3.0", "react-native-bip39": "git://github.com/celo-org/react-native-bip39#1488fa1", - "react-native-camera": "2.9.0", + "react-native-camera": "^3.7.1", "react-native-clock-sync": "^1.0.0", "react-native-config": "https://github.com/luggit/react-native-config#89a602b", - "react-native-confirm-device-credentials": "^2.0.0", - "react-native-contacts": "git://github.com/celo-org/react-native-contacts#4989b0b", - "react-native-device-info": "^2.1.0", - "react-native-firebase": "5.5.4", + "react-native-confirm-device-credentials": "git://github.com/celo-org/react-native-confirm-device-credentials#436afa9", + "react-native-contacts": "git://github.com/celo-org/react-native-contacts#fcf8f0d", + "react-native-device-info": "^4.0.1", + "react-native-exit-app": "^1.1.0", + "react-native-firebase": "^5.5.6", "react-native-flag-secure-android": "git://github.com/kristiansorens/react-native-flag-secure-android#e234251", - "react-native-fs": "^2.12.1", - "react-native-gesture-handler": "^1.1.0", - "react-native-geth": "https://github.com/celo-org/react-native-geth#8ba5091", + "react-native-fs": "^2.14.1", + "react-native-gesture-handler": "^1.4.1", + "react-native-geth": "https://github.com/celo-org/react-native-geth#6f993d9", "react-native-install-referrer": "git://github.com/celo-org/react-native-install-referrer#343bf3d", - "react-native-keep-awake": "^3.0.1", - "react-native-keyboard-aware-scroll-view": "^0.6.0", - "react-native-localize": "^1.2.1", - "react-native-mail": "^3.0.7", - "react-native-modal": "^6.1.0", - "react-native-permissions": "^1.1.1", - "react-native-progress": "^3.4.0", - "react-native-qrcode-svg": "^5.1.2", + "react-native-keep-awake": "^4.0.0", + "react-native-keyboard-aware-scroll-view": "^0.9.1", + "react-native-localize": "^1.3.0", + "react-native-mail": "^4.0.0", + "react-native-modal": "^11.4.0", + "react-native-modal-dropdown": "^0.7.0", + "react-native-permissions": "^2.0.2", + "react-native-progress": "^3.6.0", + "react-native-qrcode-svg": "^5.2.0", + "react-native-reanimated": "^1.3.0", "react-native-restart-android": "^0.0.7", - "react-native-safe-area-context": "^0.3.6", + "react-native-safe-area-context": "^0.5.0", "react-native-safe-area-view": "^1.0.0", - "react-native-screens": "^1.0.0-alpha.22", - "react-native-secure-key-store": "^2.0.4", - "react-native-secure-randombytes": "^2.2.3", - "react-native-send-intent": "git+https://github.com/celo-org/react-native-send-intent#8039938", - "react-native-sentry": "^0.43.2", + "react-native-screens": "^1.0.0-alpha.23", + "react-native-secure-key-store": "^2.0.5", + "react-native-secure-randombytes": "^3.0.0", + "react-native-send-intent": "git+https://github.com/celo-org/react-native-send-intent#a0f4b00", "react-native-shadow": "^1.2.2", - "react-native-share": "^1.1.3", - "react-native-splash-screen": "^3.1.1", - "react-native-svg": "^9.8.4", - "react-native-swiper": "^1.5.13", + "react-native-share": "^2.0.0", + "react-native-snap-carousel": "^3.8.4", + "react-native-sms": "^1.9.0", + "react-native-splash-screen": "^3.2.0", + "react-native-svg": "^9.11.1", + "react-native-swiper": "^1.5.14", "react-native-tcp": "https://github.com/cmcewen/react-native-tcp#408b674", - "react-native-udp": "^2.6.1", - "react-native-version-check": "^3.0.2", - "react-native-webview": "^5.12.1", - "react-navigation": "^3.9.0", + "react-native-udp": "git+https://github.com/celo-org/react-native-udp#730f295", + "react-native-version-check": "^3.3.0", + "react-native-webview": "^7.4.1", + "react-navigation": "^4.0.10", + "react-navigation-stack": "^1.9.4", + "react-navigation-tabs": "^2.5.6", "react-redux": "^7.1.1", "redux": "^4.0.4", - "redux-persist": "^5.9.1", - "redux-saga": "^1.0.1", - "redux-thunk": "^2.2.0", + "redux-persist": "^6.0.0", + "redux-saga": "^1.1.1", "reselect": "^3.0.1", "sleep-promise": "^8.0.1", "svgs": "^4.1.0", @@ -135,9 +139,8 @@ "web3": "1.0.0-beta.37" }, "devDependencies": { - "@babel/core": "^7.4.3", - "@babel/runtime": "^7.4.3", "@celo/typescript": "0.0.1", + "@react-native-community/eslint-config": "^0.0.5", "@types/enzyme": "3.1.10", "@types/enzyme-adapter-react-16": "1.0.4", "@types/ethereumjs-util": "^5.2.0", @@ -145,36 +148,34 @@ "@types/isomorphic-fetch": "^0.0.35", "@types/lodash": "^4.14.136", "@types/react": "^16.8.19", - "@types/react-native": "^0.57.47", + "@types/react-native": "^0.60.19", + "@types/react-native-snap-carousel": "^3.7.4", "@types/react-native-fs": "^2.8.1", "@types/react-native-keep-awake": "^2.0.1", "@types/react-redux": "^7.1.2", - "@types/react-test-renderer": "^16.0.1", + "@types/react-test-renderer": "^16.9.0", "@types/redux-mock-store": "^1.0.0", "@types/utf8": "^2.1.6", "@types/web3": "^1.0.18", "babel-core": "7.0.0-bridge.0", - "babel-jest": "^24.8.0", - "detox": "14.0.2", + "detox": "^14.5.0", "enzyme": "^3.9.0", "enzyme-adapter-react-16": "^1.11.2", "enzyme-to-json": "^3.3.5", "escape-string-regexp": "^1.0.5", "graphql-code-generator": "^0.16.1", "jest-fetch-mock": "^2.1.2", - "jest-junit": "^6.3.0", - "jest-snapshot": "^24.8.0", - "metro-react-native-babel-preset": "^0.53.1", + "metro-react-native-babel-preset": "^0.56.0", "patch-package": "^5.1.1", "postinstall-prepare": "^1.0.1", "react-devtools": "^3.6.0", - "react-dom": "16.8.3", + "react-dom": "16.9.0", "react-native-debugger-open": "^0.3.17", "react-native-kill-packager": "^1.0.0", "react-native-svg-mock": "^2.0.0", - "react-native-testing-library": "^1.9.1", + "react-native-testing-library": "^1.11.1", "react-native-version": "^3.1.0", - "react-test-renderer": "16.8.3", + "react-test-renderer": "16.9.0", "redux-mock-store": "^1.5.3", "redux-saga-test-plan": "^4.0.0-beta.2", "remote-redux-devtools": "^0.5.12" diff --git a/packages/mobile/react-native.config.js b/packages/mobile/react-native.config.js new file mode 100644 index 00000000000..635363f7056 --- /dev/null +++ b/packages/mobile/react-native.config.js @@ -0,0 +1,10 @@ +module.exports = { + dependencies: { + 'react-native-confirm-device-credentials': { + platforms: { + android: null, + ios: null, + }, + }, + }, +} diff --git a/packages/mobile/scripts/ci-e2e.sh b/packages/mobile/scripts/ci-e2e.sh deleted file mode 100755 index 91334c6f4aa..00000000000 --- a/packages/mobile/scripts/ci-e2e.sh +++ /dev/null @@ -1,32 +0,0 @@ -#!/usr/bin/env bash -export ANDROID_HOME=/usr/local/share/android-sdk -export PATH=$PATH:/usr/local/bin:/usr/sbin:/sbin:~/.nvm/versions/node/v8.12.0/bin -#Example crontab: -#*/10 * * * * cd ~/celo-monorepo/packages/mobile && scripts/ci-e2e.sh >/dev/null 2>&1 - -git pull -mkdir -p e2e/tmp -rm -r e2e/tmp/* - -( cd ../../ && yarn ) -( cd android && ./gradlew clean ) -yarn test:build-e2e && echo "Build successfull" >> e2e/tmp/last_run_log || yarn test:build-e2e -yarn test:run-e2e || rm -r e2e/tmp/* && test:yarn run-e2e >> e2e/tmp/last_run_log 2>&1 -passed=$? - -if [ $passed -eq 0 ] -then - gsutil -h "Cache-Control:private,max-age=0, no-transform" cp e2e/e2e-passing-green.svg gs://celo-e2e-data/e2e-banner.svg - gsutil acl ch -u AllUsers:R gs://celo-e2e-data/e2e-banner.svg - echo "Tests passing" >> e2e/tmp/last_run_log -else - gsutil -h "Cache-Control:private,max-age=0, no-transform" cp e2e/e2e-failing-red.svg gs://celo-e2e-data/e2e-banner.svg - gsutil acl ch -u AllUsers:R gs://celo-e2e-data/e2e-banner.svg - echo "Tests failling" >> e2e/tmp/last_run_log -fi - -tar -czvf e2e/tmp/detailed_logs.tar.gz e2e/tmp -gsutil cp e2e/tmp/detailed_logs.tar.gz gs://celo-e2e-data/detailed_logs.tar.gz -gsutil cp e2e/tmp/last_run_log gs://celo-e2e-data/last_run_log - -exit $passed diff --git a/packages/mobile/scripts/pre-deploy.sh b/packages/mobile/scripts/pre-deploy.sh index 9acb47786fd..6e80bb2618c 100755 --- a/packages/mobile/scripts/pre-deploy.sh +++ b/packages/mobile/scripts/pre-deploy.sh @@ -6,11 +6,13 @@ echo "This will prepare the mobile app for deployment to the store" echo "===Updating app version===" yarn version --no-git-tag-version echo "===Running react-native-version to update the android/ios build files===" -yarn run react-native-version +# Uses `--legacy` option (iOS only) otherwise it fails to parse the xcode project because of some specific strings +# used in the Sentry build phase script +yarn run react-native-version --legacy echo "===Done updating versions. Note: you may need to update the VERSION_CODE in gradle.properties as well if deploying manually===" echo "===Update license list and disclaimer===" -yarn update-disclaimer +yarn deploy:update-disclaimer echo "===Done updating licenses===" echo "Pre-deploy steps complete" \ No newline at end of file diff --git a/packages/mobile/scripts/pre-dev.sh b/packages/mobile/scripts/pre-dev.sh index 8dabe085faa..0d33045da86 100755 --- a/packages/mobile/scripts/pre-dev.sh +++ b/packages/mobile/scripts/pre-dev.sh @@ -10,4 +10,7 @@ ENV_FILENAME="${ENVFILE:-.env}" export $(grep -v '^#' $ENV_FILENAME | xargs) echo "Building sdk for testnet $DEFAULT_TESTNET" yarn build:sdk $DEFAULT_TESTNET -echo "Done building sdk" \ No newline at end of file +echo "Done building sdk" +echo "Jetifying react native libraries" +cd ../../ && yarn run jetify +echo "Jetified" \ No newline at end of file diff --git a/packages/mobile/scripts/run_e2e.sh b/packages/mobile/scripts/run_e2e.sh index d6e05b00d6d..38990a72a9f 100755 --- a/packages/mobile/scripts/run_e2e.sh +++ b/packages/mobile/scripts/run_e2e.sh @@ -36,7 +36,9 @@ echo "Emulator unlocked!" bash ./scripts/unlock.sh # Just to be safe kill any process that listens on the port 'yarn start' is going to use -lsof -t -i :8081 | xargs kill -9 +echo "Killing previous metro server (if any)" +react-native-kill-packager || echo 'Failed to kill for some reason' +echo "Start metro server" yarn start:bg diff --git a/packages/mobile/scripts/translateFile.js b/packages/mobile/scripts/translateFile.js new file mode 100644 index 00000000000..e6b01e940d4 --- /dev/null +++ b/packages/mobile/scripts/translateFile.js @@ -0,0 +1,62 @@ +// Translate a file using google translate +// tslint:disable: no-console + +const fs = require('fs') +const request = require('request') + +const fileName = process.argv[2] +const googleApiToken = process.argv[3] +console.info(`Translating file: ${fileName}`) + +const json = fs.readFileSync(`../locales/en-US/${fileName}`) +const strings = JSON.parse(json) +console.info(`Found ${Object.keys(strings).length} strings`) + +function translateString(s) { + return new Promise((resolve, reject) => { + console.info(`Looking up ${s}`) + request.post( + 'https://translation.googleapis.com/language/translate/v2', + { + headers: { + Authorization: `Bearer ${googleApiToken}`, + }, + json: { + format: 'text', + q: s, + source: 'en', + target: 'es', + }, + }, + (error, res, body) => { + if (error) { + reject(error) + return + } + resolve(body.data.translations[0].translatedText) + } + ) + }) +} + +async function translateStrings(stringsToTranslate) { + const translations = {} + + const promises = Promise.all( + Object.keys(stringsToTranslate).map(async (key) => { + const val = stringsToTranslate[key] + + if (typeof val === 'string') { + const t = await translateString(val) + translations[key] = t + } else if (typeof stringsToTranslate === 'object') { + translations[key] = await translateStrings(val) + } + }) + ) + + await promises + return translations +} + +translateStrings(strings).then((translations) => console.log(JSON.stringify(translations))) diff --git a/packages/mobile/scripts/unlock.sh b/packages/mobile/scripts/unlock.sh index ca9df0e4c93..bdd4d451c03 100755 --- a/packages/mobile/scripts/unlock.sh +++ b/packages/mobile/scripts/unlock.sh @@ -19,7 +19,8 @@ adb wait-for-device shell \ 'while [[ -z $(getprop sys.boot_completed) ]]; do sleep 1; done;' -echo "locksettings set-pin 123456" | adb shell +echo "locksettings set-pin $SECRET_PIN" | adb shell || echo "Failed to change pin, probably already set" + sleep 1 echo "Device is done booting" @@ -41,3 +42,5 @@ sleep 2 adb shell input text $SECRET_PIN # Input Pin sleep 1 adb shell input keyevent 66 # Enter + +echo "Done this unlock" \ No newline at end of file diff --git a/packages/mobile/secrets.json.enc b/packages/mobile/secrets.json.enc index d3c597ccdea..a95acfbc8fe 100644 Binary files a/packages/mobile/secrets.json.enc and b/packages/mobile/secrets.json.enc differ diff --git a/packages/mobile/src/account/Account.tsx b/packages/mobile/src/account/Account.tsx index dde2fdf30ed..238eb1adac3 100644 --- a/packages/mobile/src/account/Account.tsx +++ b/packages/mobile/src/account/Account.tsx @@ -2,12 +2,12 @@ import Link from '@celo/react-components/components/Link' import colors from '@celo/react-components/styles/colors' import { fontStyles } from '@celo/react-components/styles/fonts' import { anonymizedPhone, isE164Number } from '@celo/utils/src/phoneNumbers' +import * as Sentry from '@sentry/react-native' import * as React from 'react' -import { Trans, WithNamespaces, withNamespaces } from 'react-i18next' +import { WithNamespaces, withNamespaces } from 'react-i18next' import { Clipboard, ScrollView, StyleSheet, Text, TouchableOpacity, View } from 'react-native' import DeviceInfo from 'react-native-device-info' import SafeAreaView from 'react-native-safe-area-view' -import { Sentry } from 'react-native-sentry' import { connect } from 'react-redux' import { devModeTriggerClicked, resetBackupState } from 'src/account/actions' import SettingsItem from 'src/account/SettingsItem' @@ -47,6 +47,7 @@ type Props = StateProps & DispatchProps & WithNamespaces interface State { verified: boolean | undefined + version: string } const mapStateToProps = (state: RootState): StateProps => { @@ -72,12 +73,14 @@ export class Account extends React.Component { state: State = { verified: undefined, + version: '', } async componentDidMount() { const phoneNumber = this.props.e164PhoneNumber const verified = await isPhoneNumberVerified(phoneNumber) this.setState({ verified }) + this.setState({ version: DeviceInfo.getVersion() }) } goToProfile = () => { @@ -238,9 +241,7 @@ export class Account extends React.Component { )} - {/* // TODO(anna) Disabled until switch geth on/off is implemented - - */} + { {this.getDevSettingsComp()} - {DeviceInfo.getVersion() && ( - - - {t('version') + ' ' + DeviceInfo.getVersion()} - - - )} - - Test FAQ is - - here - - + {t('version') + ' ' + this.state.version} + + + {t('testFaqLink')} - - Terms of service are - - here - - + {t('termsOfServiceLink')} diff --git a/packages/mobile/src/account/Analytics.tsx b/packages/mobile/src/account/Analytics.tsx index 33cfabcfa39..da0b9801e85 100644 --- a/packages/mobile/src/account/Analytics.tsx +++ b/packages/mobile/src/account/Analytics.tsx @@ -7,7 +7,7 @@ import { ScrollView, StyleSheet, Text } from 'react-native' import { connect } from 'react-redux' import { setAnalyticsEnabled } from 'src/app/actions' import i18n, { Namespaces } from 'src/i18n' -import { headerWithCancelButton } from 'src/navigator/Headers' +import { headerWithBackButton } from 'src/navigator/Headers' import { RootState } from 'src/redux/reducers' interface StateProps { @@ -28,7 +28,7 @@ const mapStateToProps = (state: RootState): StateProps => { export class Analytics extends React.Component { static navigationOptions = () => ({ - ...headerWithCancelButton, + ...headerWithBackButton, headerTitle: i18n.t('accountScreen10:analytics'), }) diff --git a/packages/mobile/src/account/CeloLite.tsx b/packages/mobile/src/account/CeloLite.tsx index 2711ae78abb..cfeeb6763e2 100644 --- a/packages/mobile/src/account/CeloLite.tsx +++ b/packages/mobile/src/account/CeloLite.tsx @@ -6,7 +6,7 @@ import { WithNamespaces, withNamespaces } from 'react-i18next' import { ScrollView, StyleSheet, Text } from 'react-native' import { connect } from 'react-redux' import i18n, { Namespaces } from 'src/i18n' -import { headerWithCancelButton } from 'src/navigator/Headers' +import { headerWithBackButton } from 'src/navigator/Headers' import { RootState } from 'src/redux/reducers' import { setZeroSyncMode } from 'src/web3/actions' @@ -32,7 +32,7 @@ const mapStateToProps = (state: RootState): StateProps => { export class CeloLite extends React.Component { static navigationOptions = () => ({ - ...headerWithCancelButton, + ...headerWithBackButton, headerTitle: i18n.t('accountScreen10:celoLite'), }) diff --git a/packages/mobile/src/account/EditProfile.tsx b/packages/mobile/src/account/EditProfile.tsx index 76b394338c3..66a82a40506 100644 --- a/packages/mobile/src/account/EditProfile.tsx +++ b/packages/mobile/src/account/EditProfile.tsx @@ -10,7 +10,7 @@ import { setName } from 'src/account/actions' import CeloAnalytics from 'src/analytics/CeloAnalytics' import { CustomEventNames } from 'src/analytics/constants' import { Namespaces } from 'src/i18n' -import { headerWithCancelButton } from 'src/navigator/Headers' +import { headerWithBackButton } from 'src/navigator/Headers' import { navigate } from 'src/navigator/NavigationService' import { Screens } from 'src/navigator/Screens' import { RootState } from 'src/redux/reducers' @@ -32,7 +32,7 @@ const mapStateToProps = (state: RootState): StateProps => { } export class EditProfile extends React.Component { - static navigationOptions = headerWithCancelButton + static navigationOptions = headerWithBackButton state = { name: this.props.name, diff --git a/packages/mobile/src/account/Education.test.tsx b/packages/mobile/src/account/Education.test.tsx index 8b5f8964fe4..69e427dcc1a 100644 --- a/packages/mobile/src/account/Education.test.tsx +++ b/packages/mobile/src/account/Education.test.tsx @@ -1,6 +1,3 @@ -const { mockNavigationServiceFor } = require('test/utils') -const { navigateBack } = mockNavigationServiceFor('Education') - import * as React from 'react' import 'react-native' import { fireEvent, render } from 'react-native-testing-library' @@ -8,6 +5,7 @@ import { Provider } from 'react-redux' import * as renderer from 'react-test-renderer' import Education from 'src/account/Education' import { CustomEventNames } from 'src/analytics/constants' +import { navigateBack } from 'src/navigator/NavigationService' import { Screens } from 'src/navigator/Screens' import { createMockStore } from 'test/utils' diff --git a/packages/mobile/src/account/Education.tsx b/packages/mobile/src/account/Education.tsx index 471d673e468..3e2c280891f 100644 --- a/packages/mobile/src/account/Education.tsx +++ b/packages/mobile/src/account/Education.tsx @@ -4,7 +4,7 @@ import colors from '@celo/react-components/styles/colors' import { fontStyles } from '@celo/react-components/styles/fonts' import * as React from 'react' import { WithNamespaces, withNamespaces } from 'react-i18next' -import { Dimensions, Image, StyleSheet, Text, View, ViewStyle } from 'react-native' +import { Dimensions, Image, StyleSheet, Text, View } from 'react-native' import Swiper from 'react-native-swiper' import CeloAnalytics from 'src/analytics/CeloAnalytics' import { CustomEventNames } from 'src/analytics/constants' @@ -93,10 +93,8 @@ class Education extends React.Component { showsButtons={false} showsPagination={true} style={style.swiper} - // @ts-ignore - dotStyle={passiveDotStyle} - // @ts-ignore - activeDotStyle={activeDotStyle} + dotStyle={style.circlePassive} + activeDotStyle={style.circleActive} containerStyle={style.swiperContainer} > {children} @@ -145,6 +143,12 @@ class Education extends React.Component { } } +const circle = { + flex: 0, + backgroundColor: colors.inactive, + borderRadius: 8, +} + const { width } = Dimensions.get('window') const style = StyleSheet.create({ container: { @@ -189,16 +193,14 @@ const style = StyleSheet.create({ flex: 1, paddingHorizontal: 20, }, - circle: { - flex: 0, - backgroundColor: colors.inactive, - borderRadius: 8, - }, + circle, circlePassive: { + ...circle, height: PROGRESS_CIRCLE_PASSIVE_SIZE, width: PROGRESS_CIRCLE_PASSIVE_SIZE, }, circleActive: { + ...circle, height: PROGRESS_CIRCLE_ACTIVE_SIZE, width: PROGRESS_CIRCLE_ACTIVE_SIZE, }, @@ -217,7 +219,4 @@ const style = StyleSheet.create({ }, }) -const activeDotStyle: ViewStyle = StyleSheet.flatten([style.circle, style.circleActive]) -const passiveDotStyle: ViewStyle = StyleSheet.flatten([style.circle, style.circlePassive]) - export default componentWithAnalytics(withNamespaces(Namespaces.nuxCurrencyPhoto4)(Education)) diff --git a/packages/mobile/src/account/InviteReview.test.tsx b/packages/mobile/src/account/InviteReview.test.tsx index e76f174efc9..50fb8ace5e8 100644 --- a/packages/mobile/src/account/InviteReview.test.tsx +++ b/packages/mobile/src/account/InviteReview.test.tsx @@ -1,4 +1,4 @@ -import Button from '@celo/react-components/components/Button' +import mockButton from '@celo/react-components/components/Button' import * as React from 'react' import 'react-native' import { fireEvent, render } from 'react-native-testing-library' @@ -9,7 +9,7 @@ import { createMockStore, getMockI18nProps } from 'test/utils' import { mockCountryCode, mockNavigation } from 'test/values' jest.mock('src/geth/GethAwareButton', () => { - return Button + return mockButton }) jest.mock('src/identity/verification', () => { diff --git a/packages/mobile/src/account/Profile.test.tsx b/packages/mobile/src/account/Profile.test.tsx index 61083866354..7f8a1a373e2 100644 --- a/packages/mobile/src/account/Profile.test.tsx +++ b/packages/mobile/src/account/Profile.test.tsx @@ -1,10 +1,9 @@ -const { mockNavigationServiceFor } = require('test/utils') -const { navigate } = mockNavigationServiceFor('Profile') import * as React from 'react' import 'react-native' import { fireEvent, render } from 'react-native-testing-library' import { Provider } from 'react-redux' import Profile from 'src/account/Profile' +import { navigate } from 'src/navigator/NavigationService' import { Screens } from 'src/navigator/Screens' import { createMockStore } from 'test/utils' import { mockNavigation } from 'test/values' @@ -26,7 +25,6 @@ describe('Profile', () => { describe('when SettingsItem pressed', () => { it('goes to Edit Profile Screen', () => { const { getByTestId } = render(profileFactory()) - fireEvent.press(getByTestId('ProfileEditName')) expect(navigate).toBeCalledWith(Screens.EditProfile) }) diff --git a/packages/mobile/src/account/__snapshots__/Account.test.tsx.snap b/packages/mobile/src/account/__snapshots__/Account.test.tsx.snap index eb0270b754d..79c5c06b308 100644 --- a/packages/mobile/src/account/__snapshots__/Account.test.tsx.snap +++ b/packages/mobile/src/account/__snapshots__/Account.test.tsx.snap @@ -22,7 +22,9 @@ exports[`Account renders correctly 1`] = ` > + + + + celoLite + + + + + + + + + + - Test FAQ is + version + + - here + testFaqLink @@ -851,20 +943,9 @@ exports[`Account renders correctly 1`] = ` } } > - - Terms of service are - - here + termsOfServiceLink @@ -941,7 +1007,9 @@ exports[`Account renders correctly when dev mode active 1`] = ` > + + + + celoLite + + + + + + + + + + - Test FAQ is + version + + - here + testFaqLink @@ -1937,20 +2105,9 @@ exports[`Account renders correctly when dev mode active 1`] = ` } } > - - Terms of service are - - here + termsOfServiceLink diff --git a/packages/mobile/src/account/__snapshots__/DollarEducation.test.tsx.snap b/packages/mobile/src/account/__snapshots__/DollarEducation.test.tsx.snap index 72cabaa551a..b6d5c596748 100644 --- a/packages/mobile/src/account/__snapshots__/DollarEducation.test.tsx.snap +++ b/packages/mobile/src/account/__snapshots__/DollarEducation.test.tsx.snap @@ -31,6 +31,7 @@ exports[`DollarEducation renders correctly 1`] = ` > ({ - getPinFromKeystore: jest.fn(() => mockPin), - setPinInKeystore: mockSetPinInKeystore, -})) - -jest.mock('src/pincode/PincodeCache', () => ({ - getCachedPincode: jest.fn(() => mockPin), - setCachedPincode: mockSetCachedPincode, -})) +const mockPin = '123456' describe('@setPincode', () => { it('sets a PIN in phone keystore', async () => { @@ -26,7 +16,7 @@ describe('@setPincode', () => { .put(setPincodeSuccess(PincodeType.PhoneAuth)) .run() - expect(mockSetPinInKeystore).toHaveBeenCalled() + expect(setPinInKeystore).toHaveBeenCalled() }) it('returns custom PIN', async () => { @@ -34,7 +24,7 @@ describe('@setPincode', () => { .put(setPincodeSuccess(PincodeType.CustomPin)) .run() - expect(mockSetCachedPincode).toHaveBeenCalled() + expect(setCachedPincode).toHaveBeenCalled() }) it('throws error for unset pin', async () => { @@ -69,4 +59,12 @@ describe('@getPincode', () => { expect(error.message).toBe('Pin has never been set') } }) + + it('does not touch cache', async () => { + await testSaga(getPincode, false) + .next() + .next(PincodeType.CustomPin) + .next(mockPin) + .returns(mockPin) + }) }) diff --git a/packages/mobile/src/account/saga.ts b/packages/mobile/src/account/saga.ts index a0a023d4e3d..54b28170cde 100644 --- a/packages/mobile/src/account/saga.ts +++ b/packages/mobile/src/account/saga.ts @@ -12,7 +12,6 @@ import { ErrorMessages } from 'src/app/ErrorMessages' import { navigate } from 'src/navigator/NavigationService' import { Screens } from 'src/navigator/Screens' import { getCachedPincode, setCachedPincode } from 'src/pincode/PincodeCache' -// @ts-ignore TS doesn't understand the RN's platform specific file imports import { getPinFromKeystore, setPinInKeystore } from 'src/pincode/PincodeUtils' import Logger from 'src/utils/Logger' @@ -22,7 +21,7 @@ export function* setPincode({ pincodeType, pin }: SetPincodeAction) { try { if (pincodeType === PincodeType.PhoneAuth) { Logger.debug(TAG + '@setPincode', 'Setting pincode with using system auth') - pin = randomBytes(10).toString('hex') + pin = randomBytes(10).toString('hex') as string yield call(setPinInKeystore, pin) } else if (pincodeType === PincodeType.CustomPin && pin) { Logger.debug(TAG + '@setPincode', 'Pincode set using user provided pin') @@ -40,7 +39,7 @@ export function* setPincode({ pincodeType, pin }: SetPincodeAction) { } } -export function* getPincode() { +export function* getPincode(useCache = true) { const pincodeType = yield select(pincodeTypeSelector) if (pincodeType === PincodeType.Unset) { @@ -59,9 +58,11 @@ export function* getPincode() { if (pincodeType === PincodeType.CustomPin) { Logger.debug(TAG + '@getPincode', 'Getting custom pin') - const cachedPin = getCachedPincode() - if (cachedPin) { - return cachedPin + if (useCache) { + const cachedPin = getCachedPincode() + if (cachedPin) { + return cachedPin + } } const pincodeEntered = new Promise((resolve, reject) => { @@ -71,7 +72,9 @@ export function* getPincode() { if (!pin) { throw new Error('Pincode confirmation returned empty pin') } - setCachedPincode(pin) + if (useCache) { + setCachedPincode(pin) + } return pin } } diff --git a/packages/mobile/src/alert/__snapshots__/AlertBanner.test.tsx.snap b/packages/mobile/src/alert/__snapshots__/AlertBanner.test.tsx.snap index f98fd9fb569..09394d193b6 100644 --- a/packages/mobile/src/alert/__snapshots__/AlertBanner.test.tsx.snap +++ b/packages/mobile/src/alert/__snapshots__/AlertBanner.test.tsx.snap @@ -11,6 +11,8 @@ exports[`AlertBanner when error message passed in renders error message 1`] = ` > { diff --git a/packages/mobile/src/app/AppLoading.tsx b/packages/mobile/src/app/AppLoading.tsx index 421ae38fdc1..accef6de110 100644 --- a/packages/mobile/src/app/AppLoading.tsx +++ b/packages/mobile/src/app/AppLoading.tsx @@ -3,8 +3,10 @@ import colors from '@celo/react-components/styles/colors' import * as React from 'react' import { withNamespaces, WithNamespaces } from 'react-i18next' import { StyleSheet, View } from 'react-native' +import SafeAreaView from 'react-native-safe-area-view' import { Namespaces } from 'src/i18n' -import { restartApp } from 'src/utils/AppRestart' +import { RESTART_APP_I18N_KEY, restartApp } from 'src/utils/AppRestart' + const SHOW_RESTART_BUTTON_TIMEOUT = 10000 interface State { @@ -40,19 +42,19 @@ export class AppLoading extends React.Component { const { t } = this.props return ( - + {this.state.showRestartButton && (