diff --git a/.prettierignore b/.prettierignore index 89bbc62ec64..0bd1d8c2574 100644 --- a/.prettierignore +++ b/.prettierignore @@ -9,6 +9,8 @@ packages/blockchain-api/dist packages/cli/lib +packages/contractkit/lib/ + packages/walletkit/lib/ packages/walletkit/.artifacts/ packages/walletkit/contracts/ diff --git a/package.json b/package.json index e8413e51915..fc47fd667bd 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,7 @@ ] }, "devDependencies": { + "@types/jest": "^24.0.17", "husky": "^3.0.0", "lerna": "^3.16.0", "patch-package": "^5.1.1", @@ -51,7 +52,12 @@ "pretty-quick": "^1.11.1", "solc": "0.5.8", "tslint": "^5.12.1", - "typescript-tslint-plugin": "^0.5.0" + "typescript": "^3.3.3", + "jest": "^24.8.0", + "ts-jest": "^24.0.2", + "typescript-tslint-plugin": "^0.5.4", + "tsconfig-paths": "^3.8.0", + "ts-node": "^8.3.0" }, "dependencies": { "codecov": "^3.1.0" diff --git a/packages/blockchain-api/app.alfajores.yaml b/packages/blockchain-api/app.alfajores.yaml index a5be2269de1..0d51a834d78 100644 --- a/packages/blockchain-api/app.alfajores.yaml +++ b/packages/blockchain-api/app.alfajores.yaml @@ -7,6 +7,6 @@ env_variables: # Pull addresses from the build artifacts of the network in protocol/build CELO_GOLD_ADDRESS: '0x11CD75C45638Ec9f41C0e8Df78fc756201E48ff2' CELO_DOLLAR_ADDRESS: '0xd4b4fcaCAc9e23225680e89308E0a4C41Dd9C6B4' - FAUCET_ADDRESS: '0x456f41406B32c45D59E539e4BBA3D7898c3584dA' + FAUCET_ADDRESS: '0xCEa3eF8e187490A9d85A1849D98412E5D27D1Bb3' VERIFICATION_REWARDS_ADDRESS: '0xb4fdaf5f3cd313654aa357299ada901b1d2dd3b5' ABE_ADDRESS: '0x714f2879A4aa985508537f851FeBCfB26D7aF40D' \ No newline at end of file diff --git a/packages/blockchain-api/package.json b/packages/blockchain-api/package.json index a954913ca3e..de79f607e56 100644 --- a/packages/blockchain-api/package.json +++ b/packages/blockchain-api/package.json @@ -32,7 +32,7 @@ "@google-cloud/nodejs-repo-tools": "^2.3.3", "@types/dotenv": "^4.0.3", "@types/express": "^4.16.1", - "@types/jest": "^24.0.13", + "@types/jest": "^24.0.17", "@types/utf8": "^2.1.6", "@types/web3": "^1.0.18", "jest": "^24.8.0", diff --git a/packages/celotool/README.md b/packages/celotool/README.md index 728f9d45f02..cf710d2a016 100644 --- a/packages/celotool/README.md +++ b/packages/celotool/README.md @@ -78,4 +78,4 @@ a few useful commands to make running a node really easy. - Clone [Celo Blockchain repo](https://github.com/celo-org/celo-blockchain) - Build `celotooljs geth build --geth-dir -c` - Init `celotooljs geth init --geth-dir --data-dir -e ` -- Run `celotooljs geth run --geth-dir --data-dir --sync-mode ` +- Run `celotooljs geth run --geth-dir --data-dir --sync-mode ` diff --git a/packages/celotool/package.json b/packages/celotool/package.json index 116bcc3c552..322d64fee62 100644 --- a/packages/celotool/package.json +++ b/packages/celotool/package.json @@ -6,7 +6,7 @@ "author": "Celo", "license": "Apache-2.0", "dependencies": { - "@celo/walletkit": "^0.0.4", + "@celo/walletkit": "^0.0.14", "@google-cloud/monitoring": "0.7.1", "@google-cloud/pubsub": "^0.28.1", "@google-cloud/storage": "^2.4.3", @@ -25,21 +25,19 @@ "node-fetch": "^2.2.0", "prompts": "1.2.0", "sleep-promise": "^8.0.1", - "tsconfig-paths": "3.6.0", "web3": "1.0.0-beta.37", "web3-eth-admin": "1.0.0-beta.55", "yargs": "12.0.2" }, "devDependencies": { "@types/dotenv": "^4.0.3", - "@types/jest": "^24.0.13", + "@types/jest": "^24.0.17", "@types/node-fetch": "^2.1.2", "@types/prompts": "^1.1.1", "@types/web3": "^1.0.18", "@types/yargs": "^12.0.1", "jest": "^24.8.0", - "ts-jest": "^24.0.0", - "ts-node": "^7.0.1" + "ts-jest": "^24.0.0" }, "scripts": { "cli": "TS_NODE_FILES=true ts-node -r tsconfig-paths/register src/cli.ts", diff --git a/packages/celotool/src/cmds/geth/run.ts b/packages/celotool/src/cmds/geth/run.ts index 9eb3cedd6ee..ef484d3af47 100644 --- a/packages/celotool/src/cmds/geth/run.ts +++ b/packages/celotool/src/cmds/geth/run.ts @@ -32,7 +32,7 @@ export const builder = (argv: yargs.Argv) => { default: '1101', }) .option('sync-mode', { - choices: ['full', 'fast', 'light', 'celolatest', 'ultralight'], + choices: ['full', 'fast', 'light', 'ultralight'], demandOption: true, }) .option('mining', { diff --git a/packages/cli/package.json b/packages/cli/package.json index 514d645dc57..75948772b06 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,7 +1,7 @@ { "name": "@celo/celocli", "description": "CLI Tool for transacting with the Celo protocol", - "version": "0.0.6", + "version": "0.0.17", "author": "Celo", "license": "Apache-2.0", "repository": "celo-org/celo-monorepo", @@ -24,10 +24,11 @@ "build": "rm -rf lib && tsc -b", "docs": "yarn oclif-dev readme --multi --dir=../docs/command-line-interface && yarn prettier ../docs/command-line-interface/*.md --write", "lint": "tslint -c tslint.json --project tsconfig.json", + "prepublishOnly": "yarn --cwd=../walletkit build && yarn run build && oclif-dev manifest && oclif-dev readme", "test": "export TZ=UTC && jest --ci --silent" }, "dependencies": { - "@celo/walletkit": "^0.0.4", + "@celo/walletkit": "^0.0.14", "@oclif/command": "^1", "@oclif/config": "^1", "@oclif/plugin-help": "^2", @@ -60,7 +61,6 @@ "@types/node": "^10", "@types/web3": "^1.0.18", "globby": "^8", - "ts-node": "^8", "tslint": "^5", "typescript": "^3.3.3" }, @@ -71,7 +71,7 @@ "/oclif.manifest.json" ], "oclif": { - "commands": "./lib/commands", + "commands": "./lib/src/commands", "topics": { "account": { "description": "Manage your account, send and receive Celo Gold and Celo Dollars" diff --git a/packages/contractkit/.gitignore b/packages/contractkit/.gitignore new file mode 100644 index 00000000000..aa84bca4682 --- /dev/null +++ b/packages/contractkit/.gitignore @@ -0,0 +1,3 @@ +src/generated +tsconfig.tsbuildinfo +lib/ \ No newline at end of file diff --git a/packages/contractkit/.npmignore b/packages/contractkit/.npmignore new file mode 100644 index 00000000000..5d78b5e0908 --- /dev/null +++ b/packages/contractkit/.npmignore @@ -0,0 +1,7 @@ +/node_modules +/coverage +/tslint.json +/tsconfig.json +/test +/src +/jest.config.json \ No newline at end of file diff --git a/packages/contractkit/jest.config.js b/packages/contractkit/jest.config.js new file mode 100644 index 00000000000..2589a882341 --- /dev/null +++ b/packages/contractkit/jest.config.js @@ -0,0 +1,7 @@ +module.exports = { + preset: 'ts-jest', + testEnvironment: 'node', + moduleNameMapper: { + '^src/(.*)$': '/src/$1', + }, +} diff --git a/packages/contractkit/package.json b/packages/contractkit/package.json new file mode 100644 index 00000000000..4e4d5dd0a10 --- /dev/null +++ b/packages/contractkit/package.json @@ -0,0 +1,39 @@ +{ + "name": "@celo/contractkit", + "version": "0.0.5-beta3", + "description": "Celo's ContractKit to interact with Celo network", + "main": "./lib/index.js", + "types": "./lib/index.d.ts", + "author": "Celo", + "license": "Apache-2.0", + "homepage": "https://github.com/celo-org/celo-monorepo/tree/master/packages/contractkit", + "repository": "https://github.com/celo-org/celo-monorepo/tree/master/packages/contractkit", + "keywords": ["celo", "blockchain", "contractkit", "defi"], + "scripts": { + "build": "tsc -b .", + "clean": "tsc -b . --clean && rm -rf src/generated", + "build:gen": "yarn --cwd ../protocol build", + "prepublishOnly": "yarn build:gen && yarn build", + "lint": "tslint -c tslint.json --project . && tslint -c tslint.json --project test" + }, + "dependencies": { + "@celo/utils": "^0.0.6-beta5", + "@types/debug": "^4.1.5", + "bignumber.js": "^7.2.0", + "debug": "^4.1.1", + "web3": "1.0.0-beta.37", + "web3-utils": "1.0.0-beta.37" + }, + "devDependencies": { + "@celo/protocol": "1.0.0", + "@types/debug": "^4.1.5", + "@types/jest": "^24.0.17", + "@types/web3": "^1.0.18" + }, + "engines": { + "node": ">=8.13.0" + }, + "files": [ + "lib/**/*" + ] +} diff --git a/packages/contractkit/src/address-registry.ts b/packages/contractkit/src/address-registry.ts new file mode 100644 index 00000000000..c05136357c7 --- /dev/null +++ b/packages/contractkit/src/address-registry.ts @@ -0,0 +1,37 @@ +import { Address, AllContracts, CeloContract, NULL_ADDRESS } from './base' +import { newRegistry } from './generated/Registry' +import { Registry } from './generated/types/Registry' +import { ContractKit } from './kit' + +// Registry contract is always predeployed to this address +const REGISTRY_CONTRACT_ADDRESS = '0x000000000000000000000000000000000000ce10' + +export class AddressRegistry { + private readonly registry: Registry + private readonly cache: Map = new Map() + + constructor(kit: ContractKit) { + this.cache.set(CeloContract.Registry, REGISTRY_CONTRACT_ADDRESS) + this.registry = newRegistry(kit.web3, REGISTRY_CONTRACT_ADDRESS) + } + + async addressFor(contract: CeloContract): Promise
{ + if (!this.cache.has(contract)) { + const address = await this.registry.methods.getAddressFor(contract).call() + + if (!address || address === NULL_ADDRESS) { + throw new Error(`Failed to get address for ${contract} from the Registry`) + } + this.cache.set(contract, address) + } + return this.cache.get(contract)! + } + + async allAddresses(): Promise> { + const res: Partial> = {} + for (const contract of AllContracts) { + res[contract] = await this.addressFor(contract) + } + return res as Record + } +} diff --git a/packages/contractkit/src/base.ts b/packages/contractkit/src/base.ts new file mode 100644 index 00000000000..d843e0b6818 --- /dev/null +++ b/packages/contractkit/src/base.ts @@ -0,0 +1,27 @@ +export type Address = string + +export enum CeloContract { + Attestations = 'Attestations', + BondedDeposits = 'BondedDeposits', + Escrow = 'Escrow', + Exchange = 'Exchange', + GasCurrencyWhitelist = 'GasCurrencyWhitelist', + GasPriceMinimum = 'GasPriceMinimum', + GoldToken = 'GoldToken', + Governance = 'Governance', + MultiSig = 'MultiSig', + Random = 'Random', + Registry = 'Registry', + Reserve = 'Reserve', + SortedOracles = 'SortedOracles', + StableToken = 'StableToken', + Validators = 'Validators', +} + +export type CeloToken = CeloContract.GoldToken | CeloContract.StableToken + +export const AllContracts = Object.keys(CeloContract).map( + (k) => CeloContract[k as any] +) as CeloContract[] + +export const NULL_ADDRESS = '0x0000000000000000000000000000000000000000' as Address diff --git a/packages/contractkit/src/contract-cache.ts b/packages/contractkit/src/contract-cache.ts new file mode 100644 index 00000000000..d02267155a9 --- /dev/null +++ b/packages/contractkit/src/contract-cache.ts @@ -0,0 +1,104 @@ +import { CeloContract } from './base' +import { ContractKit } from './kit' +import { BondedDepositsWrapper } from './wrappers/BondedDeposits' +import { ExchangeWrapper } from './wrappers/Exchange' +import { GoldTokenWrapper } from './wrappers/GoldTokenWrapper' +import { StableTokenWrapper } from './wrappers/StableTokenWrapper' +import { ValidatorsWrapper } from './wrappers/Validators' + +const WrapperFactories = { + // [CeloContract.Attestations]: AttestationsWrapper, + [CeloContract.BondedDeposits]: BondedDepositsWrapper, + // [CeloContract.Escrow]: EscrowWrapper, + [CeloContract.Exchange]: ExchangeWrapper, + // [CeloContract.GasCurrencyWhitelist]: GasCurrencyWhitelistWrapper, + // [CeloContract.GasPriceMinimum]: GasPriceMinimumWrapper, + [CeloContract.GoldToken]: GoldTokenWrapper, + // [CeloContract.Governance]: GovernanceWrapper, + // [CeloContract.MultiSig]: MultiSigWrapper, + // [CeloContract.Random]: RandomWrapper, + // [CeloContract.Registry]: RegistryWrapper, + // [CeloContract.Reserve]: ReserveWrapper, + // [CeloContract.SortedOracles]: SortedOraclesWrapper, + [CeloContract.StableToken]: StableTokenWrapper, + [CeloContract.Validators]: ValidatorsWrapper, +} + +type CFType = typeof WrapperFactories + +interface WrapperCacheMap { + // [CeloContract.Attestations]?: AttestationsWrapper, + [CeloContract.BondedDeposits]?: BondedDepositsWrapper + // [CeloContract.Escrow]?: EscrowWrapper, + [CeloContract.Exchange]?: ExchangeWrapper + // [CeloContract.GasCurrencyWhitelist]?: GasCurrencyWhitelistWrapper, + // [CeloContract.GasPriceMinimum]?: GasPriceMinimumWrapper, + [CeloContract.GoldToken]?: GoldTokenWrapper + // [CeloContract.Governance]?: GovernanceWrapper, + // [CeloContract.MultiSig]?: MultiSigWrapper, + // [CeloContract.Random]?: RandomWrapper, + // [CeloContract.Registry]?: RegistryWrapper, + // [CeloContract.Reserve]?: ReserveWrapper, + // [CeloContract.SortedOracles]?: SortedOraclesWrapper, + [CeloContract.StableToken]?: StableTokenWrapper + [CeloContract.Validators]?: ValidatorsWrapper +} + +export class WrapperCache { + // private wrapperCache: Map = new Map() + private wrapperCache: WrapperCacheMap = {} + + constructor(readonly kit: ContractKit) {} + + // getAttestations() { + // return this.getWrapper(CeloContract.Attestations, newAttestations) + // } + getBondedDeposits() { + return this.getContract(CeloContract.BondedDeposits) + } + // getEscrow() { + // return this.getWrapper(CeloContract.Escrow, newEscrow) + // } + getExchange() { + return this.getContract(CeloContract.Exchange) + } + // getGasCurrencyWhitelist() { + // return this.getWrapper(CeloContract.GasCurrencyWhitelist, newGasCurrencyWhitelist) + // } + // getGasPriceMinimum() { + // return this.getWrapper(CeloContract.GasPriceMinimum, newGasPriceMinimum) + // } + getGoldToken() { + return this.getContract(CeloContract.GoldToken) + } + // getGovernance() { + // return this.getWrapper(CeloContract.Governance, newGovernance) + // } + // getMultiSig() { + // return this.getWrapper(CeloContract.MultiSig, newMultiSig) + // } + // getRegistry() { + // return this.getWrapper(CeloContract.Registry, newRegistry) + // } + // getReserve() { + // return this.getWrapper(CeloContract.Reserve, newReserve) + // } + // getSortedOracles() { + // return this.getWrapper(CeloContract.SortedOracles, newSortedOracles) + // } + getStableToken() { + return this.getContract(CeloContract.StableToken) + } + getValidators() { + return this.getContract(CeloContract.Validators) + } + + public async getContract(contract: C) { + if (this.wrapperCache[contract] == null) { + const instance = await this.kit._web3Contracts.getContract(contract) + const Klass: CFType[C] = WrapperFactories[contract] + this.wrapperCache[contract] = new Klass(this.kit, instance as any) as any + } + return this.wrapperCache[contract]! + } +} diff --git a/packages/contractkit/src/index.ts b/packages/contractkit/src/index.ts new file mode 100644 index 00000000000..27008311655 --- /dev/null +++ b/packages/contractkit/src/index.ts @@ -0,0 +1,8 @@ +import Web3 from 'web3' + +export { Address, CeloContract, CeloToken, NULL_ADDRESS } from './base' +export * from './kit' + +export function newWeb3(url: string) { + return new Web3(url) +} diff --git a/packages/contractkit/src/kit.ts b/packages/contractkit/src/kit.ts new file mode 100644 index 00000000000..b23b43b6124 --- /dev/null +++ b/packages/contractkit/src/kit.ts @@ -0,0 +1,74 @@ +import Web3 from 'web3' +import { TransactionObject, Tx } from 'web3/eth/types' +import { AddressRegistry } from './address-registry' +import { Address, CeloContract, CeloToken } from './base' +import { WrapperCache } from './contract-cache' +import { sendTransaction, TxOptions } from './utils/send-tx' +import { toTxResult, TransactionResult } from './utils/tx-result' +import { Web3ContractCache } from './web3-contract-cache' + +export function newKit(url: string) { + return newKitFromWeb3(new Web3(url)) +} + +export function newKitFromWeb3(web3: Web3) { + return new ContractKit(web3) +} + +export class ContractKit { + readonly registry: AddressRegistry + readonly _web3Contracts: Web3ContractCache + readonly contracts: WrapperCache + + defaultOptions: TxOptions + constructor(readonly web3: Web3) { + this.defaultOptions = { + gasInflationFactor: 1.3, + } + + this.registry = new AddressRegistry(this) + this._web3Contracts = new Web3ContractCache(this) + this.contracts = new WrapperCache(this) + } + + async setGasCurrency(token: CeloToken) { + this.defaultOptions.gasCurrency = + token === CeloContract.GoldToken ? undefined : await this.registry.addressFor(token) + } + + set defaultAccount(address: Address) { + this.web3.eth.defaultAccount = address + } + + get defaultAccount(): Address { + return this.web3.eth.defaultAccount + } + + setGasCurrencyAddress(address: Address) { + this.defaultOptions.gasCurrency = address + } + + sendTransaction(tx: Tx): TransactionResult { + const promiEvent = this.web3.eth.sendTransaction({ + from: this.defaultOptions.from, + // TODO this won't work for locally signed TX + gasPrice: '0', + // @ts-ignore + gasCurrency: this.defaultOptions.gasCurrency, + // TODO needed for locally signed tx, ignored by now (celo-blockchain with set it) + // gasFeeRecipient: this.defaultOptions.gasFeeRecipient, + ...tx, + }) + return toTxResult(promiEvent) + } + + sendTransactionObject( + txObj: TransactionObject, + options?: TxOptions + ): Promise { + return sendTransaction(txObj, { + ...this.defaultOptions, + ...options, + }) + } +} diff --git a/packages/contractkit/src/utils/external-promise.ts b/packages/contractkit/src/utils/external-promise.ts new file mode 100644 index 00000000000..a02c8329174 --- /dev/null +++ b/packages/contractkit/src/utils/external-promise.ts @@ -0,0 +1,11 @@ +export class ExternalPromise extends Promise { + public resolve!: (value: T) => void + public reject!: (err: any) => void + + constructor() { + super((resolve, reject) => { + this.resolve = resolve + this.reject = reject + }) + } +} diff --git a/packages/contractkit/src/utils/send-tx.ts b/packages/contractkit/src/utils/send-tx.ts new file mode 100644 index 00000000000..05cccc43185 --- /dev/null +++ b/packages/contractkit/src/utils/send-tx.ts @@ -0,0 +1,51 @@ +import debugFactory from 'debug' +import { TransactionObject } from 'web3/eth/types' +import { Address } from '../base' +import { toTxResult, TransactionResult } from './tx-result' + +const debug = debugFactory('contractkit:sendtx') + +export interface TxOptions { + gasInflationFactor?: number + gasFeeRecipient?: Address + gasCurrency?: Address | undefined + from?: Address + estimatedGas?: number | undefined +} + +/** + * sendTransaction mainly abstracts the sending of a transaction in a promise like + * interface. + */ +export async function sendTransaction( + tx: TransactionObject, + txOptions: TxOptions = {} +): Promise { + const txParams: any = { + from: txOptions.from, + gasCurrency: txOptions.gasCurrency, + gasPrice: '0', + } + + let gas = txOptions.estimatedGas + if (gas === undefined) { + const inflactionFactor = txOptions.gasInflationFactor || 1 + gas = Math.round((await tx.estimateGas(txParams)) * inflactionFactor) + debug('estimatedGas: %s', gas) + // logger(EstimatedGas(estimatedGas)) + } + + const promiEvent = tx.send({ + from: txOptions.from, + // @ts-ignore + gasCurrency: txOptions.gasCurrency, + // TODO needed for locally signed tx, ignored by now (celo-blockchain with set it) + // gasFeeRecipient: txOptions.gasFeeRecipient, + gas, + // Hack to prevent web3 from adding the suggested gold gas price, allowing geth to add + // the suggested price in the selected gasCurrency. + // TODO this won't work for locally signed TX + gasPrice: '0', + }) + return toTxResult(promiEvent) +} diff --git a/packages/contractkit/src/utils/tx-result.ts b/packages/contractkit/src/utils/tx-result.ts new file mode 100644 index 00000000000..f4736c5e04b --- /dev/null +++ b/packages/contractkit/src/utils/tx-result.ts @@ -0,0 +1,44 @@ +import debugFactory from 'debug' +import PromiEvent from 'web3/promiEvent' +import { TransactionReceipt } from 'web3/types' +import { ExternalPromise } from './external-promise' + +const debug = debugFactory('contractkit:txresult') + +export function toTxResult(pe: PromiEvent) { + return new TransactionResult(pe) +} + +export class TransactionResult { + private hashFuture = new ExternalPromise() + private receiptFuture = new ExternalPromise() + + constructor(pe: PromiEvent) { + pe.on('transactionHash', (hash: string) => { + debug('hash: %s', hash) + this.hashFuture.resolve(hash) + }) + .on('receipt', (receipt: TransactionReceipt) => { + debug('receipt: %O', receipt) + this.receiptFuture.resolve(receipt) + }) + + .on('error', ((error: any, receipt: TransactionReceipt | false) => { + if (!receipt) { + debug('send-error: %o', error) + this.hashFuture.reject(error) + } else { + debug('mining-error: %o, %O', error, receipt) + } + this.receiptFuture.reject(error) + }) as any) + } + + getHash() { + return this.hashFuture + } + + waitReceipt() { + return this.receiptFuture + } +} diff --git a/packages/contractkit/src/web3-contract-cache.ts b/packages/contractkit/src/web3-contract-cache.ts new file mode 100644 index 00000000000..8c23e43551c --- /dev/null +++ b/packages/contractkit/src/web3-contract-cache.ts @@ -0,0 +1,102 @@ +import { CeloContract } from './base' +import { newAttestations } from './generated/Attestations' +import { newBondedDeposits } from './generated/BondedDeposits' +import { newEscrow } from './generated/Escrow' +import { newExchange } from './generated/Exchange' +import { newGasCurrencyWhitelist } from './generated/GasCurrencyWhitelist' +import { newGasPriceMinimum } from './generated/GasPriceMinimum' +import { newGoldToken } from './generated/GoldToken' +import { newGovernance } from './generated/Governance' +import { newMultiSig } from './generated/MultiSig' +import { newRandom } from './generated/Random' +import { newRegistry } from './generated/Registry' +import { newReserve } from './generated/Reserve' +import { newSortedOracles } from './generated/SortedOracles' +import { newStableToken } from './generated/StableToken' +import { newValidators } from './generated/Validators' +import { ContractKit } from './kit' + +const ContractFactories = { + [CeloContract.Attestations]: newAttestations, + [CeloContract.BondedDeposits]: newBondedDeposits, + [CeloContract.Escrow]: newEscrow, + [CeloContract.Exchange]: newExchange, + [CeloContract.GasCurrencyWhitelist]: newGasCurrencyWhitelist, + [CeloContract.GasPriceMinimum]: newGasPriceMinimum, + [CeloContract.GoldToken]: newGoldToken, + [CeloContract.Governance]: newGovernance, + [CeloContract.MultiSig]: newMultiSig, + [CeloContract.Random]: newRandom, + [CeloContract.Registry]: newRegistry, + [CeloContract.Reserve]: newReserve, + [CeloContract.SortedOracles]: newSortedOracles, + [CeloContract.StableToken]: newStableToken, + [CeloContract.Validators]: newValidators, +} + +type CFType = typeof ContractFactories +type ContractCacheMap = { [K in keyof CFType]?: ReturnType } + +export class Web3ContractCache { + private cacheMap: ContractCacheMap = {} + + constructor(readonly kit: ContractKit) {} + + getAttestations() { + return this.getContract(CeloContract.Attestations) + } + getBondedDeposits() { + return this.getContract(CeloContract.BondedDeposits) + } + getEscrow() { + return this.getContract(CeloContract.Escrow) + } + getExchange() { + return this.getContract(CeloContract.Exchange) + } + getGasCurrencyWhitelist() { + return this.getContract(CeloContract.GasCurrencyWhitelist) + } + getGasPriceMinimum() { + return this.getContract(CeloContract.GasPriceMinimum) + } + getGoldToken() { + return this.getContract(CeloContract.GoldToken) + } + getGovernance() { + return this.getContract(CeloContract.Governance) + } + getMultiSig() { + return this.getContract(CeloContract.MultiSig) + } + getRandom() { + return this.getContract(CeloContract.Random) + } + getRegistry() { + return this.getContract(CeloContract.Registry) + } + getReserve() { + return this.getContract(CeloContract.Reserve) + } + getSortedOracles() { + return this.getContract(CeloContract.SortedOracles) + } + getStableToken() { + return this.getContract(CeloContract.StableToken) + } + getValidators() { + return this.getContract(CeloContract.Validators) + } + + async getContract(contract: C) { + if (this.cacheMap[contract] == null) { + const createFn = ContractFactories[contract] as CFType[C] + this.cacheMap[contract] = createFn( + this.kit.web3, + await this.kit.registry.addressFor(contract) + ) as NonNullable + } + // we know it's defined (thus the !) + return this.cacheMap[contract]! + } +} diff --git a/packages/contractkit/src/wrappers/BaseWrapper.ts b/packages/contractkit/src/wrappers/BaseWrapper.ts new file mode 100644 index 00000000000..7327539d4b5 --- /dev/null +++ b/packages/contractkit/src/wrappers/BaseWrapper.ts @@ -0,0 +1,39 @@ +import { TransactionObject } from 'web3/eth/types' +import { ContractKit } from '../kit' +import { TxOptions } from '../utils/send-tx' +import { TransactionResult } from '../utils/tx-result' + +type Method = (...args: I) => TransactionObject + +export abstract class BaseWrapper { + constructor(protected readonly kit: ContractKit, protected readonly contract: T) {} + + protected proxySend(methodFn: Method) { + return (...args: I) => this.wrapSend(methodFn(...args)) + } + protected proxyCall(methodFn: Method) { + return (...args: I) => methodFn(...args).call() + } + + protected proxyCallAndTransform( + methodFn: Method, + post: (input: O) => F + ) { + return (...args: I) => + methodFn(...args) + .call() + .then(post) + } + + protected wrapSend(txo: TransactionObject): CeloTransactionObject { + return { + send: (options?: TxOptions) => this.kit.sendTransactionObject(txo, options), + txo, + } + } +} + +export interface CeloTransactionObject { + txo: TransactionObject + send(options?: TxOptions): Promise +} diff --git a/packages/contractkit/src/wrappers/BondedDeposits.ts b/packages/contractkit/src/wrappers/BondedDeposits.ts new file mode 100644 index 00000000000..1f711de8b48 --- /dev/null +++ b/packages/contractkit/src/wrappers/BondedDeposits.ts @@ -0,0 +1,130 @@ +import { zip } from '@celo/utils/lib/src/collections' +import BN from 'bn.js' +import Web3 from 'web3' +import { TransactionObject } from 'web3/eth/types' +import { Address } from '../base' +import { BondedDeposits } from '../generated/types/BondedDeposits' +import { BaseWrapper } from '../wrappers/BaseWrapper' + +export interface VotingDetails { + accountAddress: Address + voterAddress: Address + weight: BN +} + +interface Deposit { + time: BN + value: BN +} + +export interface Deposits { + bonded: Deposit[] + notified: Deposit[] + total: { + gold: BN + weight: BN + } +} + +enum Roles { + validating, + voting, + rewards, +} + +export class BondedDepositsWrapper extends BaseWrapper { + async getAccountWeight(account: Address): Promise { + const accountWeight = await this.contract.methods.getAccountWeight(account).call() + return Web3.utils.toBN(accountWeight) + } + + async getVotingDetails(accountOrVoterAddress: Address): Promise { + const accountAddress = await this.contract.methods + .getAccountFromDelegateAndRole(accountOrVoterAddress, Roles.voting) + .call() + + return { + accountAddress, + voterAddress: accountOrVoterAddress, + weight: await this.getAccountWeight(accountAddress), + } + } + + async getBondedDepositValue(account: string, noticePeriod: string): Promise { + const deposit = await this.contract.methods.getBondedDeposit(account, noticePeriod).call() + return this.getValueFromDeposit(deposit) + } + + async getBondedDeposits(account: string): Promise { + return this.zipAccountTimesAndValuesToDeposits( + account, + this.contract.methods.getNoticePeriods, + this.getBondedDepositValue.bind(this) + ) + } + + async getNotifiedDepositValue(account: string, availTime: string): Promise { + const deposit = await this.contract.methods.getNotifiedDeposit(account, availTime).call() + return this.getValueFromDeposit(deposit) + } + + async getNotifiedDeposits(account: string): Promise { + return this.zipAccountTimesAndValuesToDeposits( + account, + this.contract.methods.getAvailabilityTimes, + this.getNotifiedDepositValue.bind(this) + ) + } + + async getDeposits(account: string): Promise { + const bonded = await this.getBondedDeposits(account) + const notified = await this.getNotifiedDeposits(account) + const weight = await this.getAccountWeight(account) + + let gold = new BN(0) + bonded.forEach((bond) => (gold = gold.add(bond.value))) + notified.forEach((bond) => (gold = gold.add(bond.value))) + + return { + bonded, + notified, + total: { weight, gold }, + } + } + + // FIXME this.contract.methods.delegateRewards does not exist + async delegateRewardsTx(account: string, delegate: string): Promise> { + const sig = await this.getParsedSignatureOfAddress(account, delegate) + + return this.contract.methods.delegateRole(Roles.rewards, delegate, sig.v, sig.r, sig.s) + } + + private getValueFromDeposit(deposit: { 0: string; 1: string }) { + return Web3.utils.toBN(deposit[0]) + } + + private async getParsedSignatureOfAddress(address: string, 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, + } + } + + private async zipAccountTimesAndValuesToDeposits( + account: string, + timesFunc: (account: string) => TransactionObject, + valueFunc: (account: string, time: string) => Promise + ) { + const accountTimes = await timesFunc(account).call() + const accountValues = await Promise.all(accountTimes.map((time) => valueFunc(account, time))) + return zip( + // tslint:disable-next-line: no-object-literal-type-assertion + (time, value) => ({ time, value } as Deposit), + accountTimes.map((time) => Web3.utils.toBN(time)), + accountValues + ) + } +} diff --git a/packages/contractkit/src/wrappers/Exchange.ts b/packages/contractkit/src/wrappers/Exchange.ts new file mode 100644 index 00000000000..1e4218b64c8 --- /dev/null +++ b/packages/contractkit/src/wrappers/Exchange.ts @@ -0,0 +1,15 @@ +import { Exchange } from '../generated/types/Exchange' +import { BaseWrapper } from './BaseWrapper' + +export class ExchangeWrapper extends BaseWrapper { + getBuyTokenAmount = this.proxyCall(this.contract.methods.getBuyTokenAmount) + + getSellTokenAmount = this.proxyCall(this.contract.methods.getSellTokenAmount) + + getBuyAndSellBuckets = this.proxyCallAndTransform( + this.contract.methods.getBuyAndSellBuckets, + (callRes) => [callRes[0], callRes[1]] as [string, string] + ) + + exchange = this.proxySend(this.contract.methods.exchange) +} diff --git a/packages/contractkit/src/wrappers/GoldTokenWrapper.ts b/packages/contractkit/src/wrappers/GoldTokenWrapper.ts new file mode 100644 index 00000000000..3e16a9750c1 --- /dev/null +++ b/packages/contractkit/src/wrappers/GoldTokenWrapper.ts @@ -0,0 +1,16 @@ +import { Address } from '../base' +import { GoldToken } from '../generated/types/GoldToken' +import { BaseWrapper } from './BaseWrapper' + +export class GoldTokenWrapper extends BaseWrapper { + allowance = this.proxyCall(this.contract.methods.allowance) + name = this.proxyCall(this.contract.methods.name) + symbol = this.proxyCall(this.contract.methods.symbol) + decimals = this.proxyCall(this.contract.methods.decimals) + totalSupply = this.proxyCall(this.contract.methods.totalSupply) + approve = this.proxySend(this.contract.methods.approve) + transferWithComment = this.proxySend(this.contract.methods.transferWithComment) + transfer = this.proxySend(this.contract.methods.transfer) + transferFrom = this.proxySend(this.contract.methods.transferFrom) + balanceOf = (account: Address) => this.kit.web3.eth.getBalance(account) +} diff --git a/packages/contractkit/src/wrappers/StableTokenWrapper.ts b/packages/contractkit/src/wrappers/StableTokenWrapper.ts new file mode 100644 index 00000000000..3cf2446aba7 --- /dev/null +++ b/packages/contractkit/src/wrappers/StableTokenWrapper.ts @@ -0,0 +1,23 @@ +import { StableToken } from '../generated/types/StableToken' +import { BaseWrapper } from './BaseWrapper' + +export class StableTokenWrapper extends BaseWrapper { + allowance = this.proxyCall(this.contract.methods.allowance) + balanceOf = this.proxyCall(this.contract.methods.balanceOf) + minter = this.proxyCall(this.contract.methods.minter) + name = this.proxyCall(this.contract.methods.name) + symbol = this.proxyCall(this.contract.methods.symbol) + decimals = this.proxyCall(this.contract.methods.decimals) + owner = this.proxyCall(this.contract.methods.owner) + totalSupply = this.proxyCall(this.contract.methods.totalSupply) + getInflationParameters = this.proxyCall(this.contract.methods.getInflationParameters) + valueToUnits = this.proxyCall(this.contract.methods.valueToUnits) + unitsToValue = this.proxyCall(this.contract.methods.unitsToValue) + approve = this.proxySend(this.contract.methods.approve) + mint = this.proxySend(this.contract.methods.mint) + burn = this.proxySend(this.contract.methods.burn) + transferWithComment = this.proxySend(this.contract.methods.transferWithComment) + transfer = this.proxySend(this.contract.methods.transfer) + transferFrom = this.proxySend(this.contract.methods.transferFrom) + setInflationParameters = this.proxySend(this.contract.methods.setInflationParameters) +} diff --git a/packages/contractkit/src/wrappers/Validators.ts b/packages/contractkit/src/wrappers/Validators.ts new file mode 100644 index 00000000000..e8ea338ee46 --- /dev/null +++ b/packages/contractkit/src/wrappers/Validators.ts @@ -0,0 +1,145 @@ +import { eqAddress } from '@celo/utils/lib/src/address' +import { compareBN } from '@celo/utils/lib/src/bn' +import { zip } from '@celo/utils/lib/src/collections' +import Web3 from 'web3' +import { TransactionObject } from 'web3/eth/types' +import { Address, NULL_ADDRESS } from '../base' +import { Validators } from '../generated/types/Validators' +import { BaseWrapper } from './BaseWrapper' + +import BN = require('bn.js') + +export interface Validator { + address: Address + id: string + name: string + url: string + publicKey: string + affiliation: string | null +} + +export interface ValidatorGroup { + address: Address + id: string + name: string + url: string + members: Address[] +} + +export interface ValidatorGroupVote { + address: Address + votes: BN +} + +export class ValidatorsWrapper extends BaseWrapper { + async getRegisteredValidators(): Promise { + const vgAddresses = await this.contract.methods.getRegisteredValidators().call() + + return Promise.all(vgAddresses.map((addr) => this.getValidator(addr))) + } + + async getValidator(address: Address): Promise { + const res = await this.contract.methods.getValidator(address).call() + return { + address, + id: res[0], + name: res[1], + url: res[2], + publicKey: res[3] as any, + affiliation: res[4], + } + } + + async getRegisteredValidatorGroups(): Promise { + const vgAddresses = await this.contract.methods.getRegisteredValidatorGroups().call() + return Promise.all(vgAddresses.map((addr) => this.getValidatorGroup(addr))) + } + + async getValidatorGroup(address: Address): Promise { + const res = await this.contract.methods.getValidatorGroup(address).call() + return { address, id: res[0], name: res[1], url: res[2], members: res[3] } + } + + async getValidatorGroupsVotes(): Promise { + const vgAddresses = await this.contract.methods.getRegisteredValidatorGroups().call() + const res = await this.contract.methods.getValidatorGroupVotes().call() + const r = zip((a, b) => ({ address: a, votes: Web3.utils.toBN(b) }), res[0], res[1]) + for (const vgAddress of vgAddresses) { + if (!res[0].includes(vgAddress)) { + r.push({ address: vgAddress, votes: Web3.utils.toBN(0) }) + } + } + return r + } + + async getVoteFrom(validatorAddress: Address): Promise
{ + return this.contract.methods.voters(validatorAddress).call() + } + + async revokeVote(): Promise> { + if (this.kit.defaultAccount == null) { + throw new Error(`missing from at new ValdidatorUtils()`) + } + + const bondedDeposits = await this.kit.contracts.getBondedDeposits() + const votingDetails = await bondedDeposits.getVotingDetails(this.kit.defaultAccount) + const votedGroup = await this.getVoteFrom(votingDetails.accountAddress) + + if (votedGroup == null) { + throw new Error(`Not current vote for ${this.kit.defaultAccount}`) + } + + const { lesser, greater } = await this.findLesserAndGreaterAfterVote( + votedGroup, + votingDetails.weight.neg() + ) + + return this.contract.methods.revokeVote(lesser, greater) + } + + async vote(validatorGroup: Address): Promise> { + if (this.kit.defaultAccount == null) { + throw new Error(`missing from at new ValdidatorUtils()`) + } + + const bondedDeposits = await this.kit.contracts.getBondedDeposits() + const votingDetails = await bondedDeposits.getVotingDetails(this.kit.defaultAccount) + + const { lesser, greater } = await this.findLesserAndGreaterAfterVote( + validatorGroup, + votingDetails.weight + ) + + return this.contract.methods.vote(validatorGroup, lesser, greater) + } + + private async findLesserAndGreaterAfterVote( + votedGroup: Address, + voteWeight: BN + ): Promise<{ lesser: Address; greater: Address }> { + const currentVotes = await this.getValidatorGroupsVotes() + + const selectedGroup = currentVotes.find((cv) => eqAddress(cv.address, votedGroup)) + + // modify the list + if (selectedGroup) { + selectedGroup.votes = selectedGroup.votes.add(voteWeight) + } else { + currentVotes.push({ + address: votedGroup, + votes: voteWeight, + }) + } + + // re-sort + currentVotes.sort((a, b) => compareBN(a.votes, b.votes)) + + // find new index + const newIdx = currentVotes.findIndex((cv) => eqAddress(cv.address, votedGroup)) + + return { + lesser: newIdx === 0 ? NULL_ADDRESS : currentVotes[newIdx - 1].address, + greater: newIdx === currentVotes.length - 1 ? NULL_ADDRESS : currentVotes[newIdx + 1].address, + } + } +} diff --git a/packages/contractkit/test/tsconfig.json b/packages/contractkit/test/tsconfig.json new file mode 100644 index 00000000000..0a9aebb2d29 --- /dev/null +++ b/packages/contractkit/test/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../../typescript/tsconfig.library.json", + "compilerOptions": { + "rootDir": ".", + "noEmit": true, + "baseUrl": "." + }, + "include": ["."], + "references": [{ "path": ".." }] +} diff --git a/packages/contractkit/tsconfig.json b/packages/contractkit/tsconfig.json new file mode 100644 index 00000000000..c163b866107 --- /dev/null +++ b/packages/contractkit/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../typescript/tsconfig.library.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "lib" + }, + "include": ["src", "types/"] +} diff --git a/packages/contractkit/tslint.json b/packages/contractkit/tslint.json new file mode 100644 index 00000000000..dfc1be41d87 --- /dev/null +++ b/packages/contractkit/tslint.json @@ -0,0 +1,7 @@ +{ + "extends": ["@celo/typescript/tslint.json"], + "rules": { + "no-global-arrow-functions": false, + "no-console": false + } +} diff --git a/packages/contractkit/types/web3.d.ts b/packages/contractkit/types/web3.d.ts new file mode 100644 index 00000000000..ff242361673 --- /dev/null +++ b/packages/contractkit/types/web3.d.ts @@ -0,0 +1,8 @@ +import 'web3/eth/types' + +declare module 'web3/eth/types' { + export interface Tx { + // gasFeeRecipient?: string + gasCurrency?: string + } +} diff --git a/packages/dappkit/package.json b/packages/dappkit/package.json index 60a88209aeb..8dea0e9ae69 100644 --- a/packages/dappkit/package.json +++ b/packages/dappkit/package.json @@ -1,13 +1,15 @@ { "name": "@celo/dappkit", - "version": "0.0.1-j", + "version": "0.0.7", "scripts": { "build": "tsc" }, "dependencies": { - "@celo/walletkit": "0.0.4", - "@celo/utils": "0.0.5", - "expo": "^34.0.1" + "@celo/walletkit": "0.0.14", + "@celo/utils": "0.0.6-beta5", + "expo": "^34.0.1", + "expo-contacts": "6.0.0", + "libphonenumber-js": "^1.7.22" }, "devDependencies": { "typescript": "3.3.3", diff --git a/packages/dappkit/src/index.ts b/packages/dappkit/src/index.ts index abc11d3b6f0..51ca4b93f67 100644 --- a/packages/dappkit/src/index.ts +++ b/packages/dappkit/src/index.ts @@ -2,14 +2,25 @@ import { AccountAuthRequest, DappKitRequestMeta, DappKitRequestTypes, + DappKitResponse, DappKitResponseStatus, - parseDappkitResponseDepplink, + parseDappkitResponseDeeplink, + PhoneNumberUtils, serializeDappKitRequestDeeplink, SignTxRequest, TxToSignParam, } from '@celo/utils' -import { CeloTokenType, GoldToken, StableToken } from '@celo/walletkit' +import { + Attestations, + CeloTokenType, + GoldToken, + lookupPhoneNumbers, + StableToken, +} from '@celo/walletkit' import { Linking } from 'expo' +import { Contact, Fields, getContactsAsync, PhoneNumber } from 'expo-contacts' +import { E164Number, parsePhoneNumberFromString } from 'libphonenumber-js' +import { chunk, flatMap, flatten, fromPairs, zipObject } from 'lodash' import Web3 from 'web3' import { TransactionObject } from 'web3/eth/types' @@ -23,7 +34,7 @@ export { export function listenToAccount(callback: (account: string) => void) { return Linking.addEventListener('url', ({ url }: { url: string }) => { try { - const dappKitResponse = parseDappkitResponseDepplink(url) + const dappKitResponse = parseDappkitResponseDeeplink(url) if ( dappKitResponse.type === DappKitRequestTypes.ACCOUNT_ADDRESS && dappKitResponse.status === DappKitResponseStatus.SUCCESS @@ -34,10 +45,52 @@ export function listenToAccount(callback: (account: string) => void) { }) } +export function waitForAccountAuth(requestId: string): Promise { + return new Promise((resolve, reject) => { + const handler = ({ url }: { url: string }) => { + try { + const dappKitResponse = parseDappkitResponseDeeplink(url) + if ( + requestId === dappKitResponse.requestId && + dappKitResponse.type === DappKitRequestTypes.ACCOUNT_ADDRESS && + dappKitResponse.status === DappKitResponseStatus.SUCCESS + ) { + Linking.removeEventListener('url', handler) + resolve(dappKitResponse) + } + } catch (error) { + reject(error) + } + } + Linking.addEventListener('url', handler) + }) +} + +export function waitForSignedTxs(requestId: string): Promise { + return new Promise((resolve, reject) => { + const handler = ({ url }: { url: string }) => { + try { + const dappKitResponse = parseDappkitResponseDeeplink(url) + if ( + requestId === dappKitResponse.requestId && + dappKitResponse.type === DappKitRequestTypes.SIGN_TX && + dappKitResponse.status === DappKitResponseStatus.SUCCESS + ) { + Linking.removeEventListener('url', handler) + resolve(dappKitResponse) + } + } catch (error) { + reject(error) + } + } + Linking.addEventListener('url', handler) + }) +} + export function listenToSignedTxs(callback: (signedTxs: string[]) => void) { return Linking.addEventListener('url', ({ url }: { url: string }) => { try { - const dappKitResponse = parseDappkitResponseDepplink(url) + const dappKitResponse = parseDappkitResponseDeeplink(url) if ( dappKitResponse.type === DappKitRequestTypes.SIGN_TX && dappKitResponse.status === DappKitResponseStatus.SUCCESS @@ -72,11 +125,12 @@ async function getGasCurrencyContract( } export interface TxParams { - txId: string tx: TransactionObject from: string to: string gasCurrency: GasCurrency + estimatedGas?: number + value?: string } export async function requestTxSig( @@ -84,29 +138,152 @@ export async function requestTxSig( txParams: TxParams[], meta: DappKitRequestMeta ) { + // TODO: For multi-tx payloads, we for now just assume the same from address for all txs. We should apply a better heuristic + const baseNonce = await web3.eth.getTransactionCount(txParams[0].from) const txs: TxToSignParam[] = await Promise.all( - txParams.map(async (txParam) => { + txParams.map(async (txParam, index) => { const gasCurrencyContract = await getGasCurrencyContract(web3, txParam.gasCurrency) + const value = txParam.value === undefined ? '0' : txParam.value + const estimatedTxParams = { gasCurrency: gasCurrencyContract.options.address, - } - // @ts-ignore - const estimatedGas = await txParam.tx.estimateGas(estimatedTxParams) + from: txParam.from, + value, + } as any + const estimatedGas = + txParam.estimatedGas === undefined + ? await txParam.tx.estimateGas(estimatedTxParams) + : txParam.estimatedGas - const nonce = await web3.eth.getTransactionCount(txParam.from) return { txData: txParam.tx.encodeABI(), estimatedGas, - nonce, + nonce: baseNonce + index, gasCurrencyAddress: gasCurrencyContract._address, + value, ...txParam, } }) ) - - // const url = Linking.makeUrl(returnPath) - const request = SignTxRequest(txs, meta) Linking.openURL(serializeDappKitRequestDeeplink(request)) } + +function isValidPhoneNumber(phoneNumber: PhoneNumber): E164Number | undefined { + if (phoneNumber.number === undefined) { + return undefined + } + const parsedPhoneNumber = parsePhoneNumberFromString(phoneNumber.number) + + if (parsedPhoneNumber === undefined) { + return undefined + } + + if (!parsedPhoneNumber.isValid()) { + return undefined + } + + return parsedPhoneNumber.number +} + +export interface PhoneNumberMappingEntry { + address: string + phoneNumber: string + id: string + attestationStat: { + total: number + completed: number + } +} + +function createPhoneNumberToContactMapping(contacts: Contact[]) { + const phoneNumberObjects = flatMap(contacts, (contact) => { + return contact.phoneNumbers + ? contact.phoneNumbers.flatMap((phoneNumber) => { + const e164Number = isValidPhoneNumber(phoneNumber) + return e164Number ? { e164Number, id: contact.id } : [] + }) + : [] + }) as [{ e164Number: E164Number; id: string }] + const flattened = phoneNumberObjects.map(({ e164Number, id }) => [e164Number.toString(), id]) + return fromPairs(flattened) +} + +async function lookupPhoneNumbersOnAttestations( + web3: Web3, + allPhoneNumbers: { [phoneNumber: string]: string } +): Promise<{ [address: string]: PhoneNumberMappingEntry }> { + const attestations = await Attestations(web3) + const nestedResult = await Promise.all( + chunk(Object.keys(allPhoneNumbers), 20).map(async (phoneNumbers) => { + const hashedPhoneNumbers = phoneNumbers.map(PhoneNumberUtils.getPhoneHash) + + const phoneNumbersByHash = zipObject(hashedPhoneNumbers, phoneNumbers) + + const result = await lookupPhoneNumbers(attestations, hashedPhoneNumbers) + + return flatMap(Object.entries(result), ([phoneHash, attestationStats]) => + Object.entries(attestationStats).map(([address, attestationStat]) => ({ + address, + phoneNumber: phoneNumbersByHash[phoneHash], + id: allPhoneNumbers[phoneNumbersByHash[phoneHash]], + attestationStat, + })) + ) + }) + ) + + return fromPairs(flatten(nestedResult).map((entry) => [entry.address, entry])) +} + +export interface ContactsById { + [id: string]: Contact +} +export interface PhoneNumberMappingEntryByAddress { + [address: string]: PhoneNumberMappingEntry +} +export async function fetchContacts( + web3: Web3 +): Promise<{ rawContacts: ContactsById; phoneNumbersByAddress: PhoneNumberMappingEntryByAddress }> { + const contacts = await getContactsAsync({ + fields: [Fields.PhoneNumbers, Fields.Image], + }) + + const filteredContacts = contacts.data.filter((contact) => { + return ( + contact.phoneNumbers && contact.phoneNumbers.some((p) => isValidPhoneNumber(p) !== undefined) + ) + }) + + const rawContacts = fromPairs(filteredContacts.map((contact) => [contact.id, contact])) + + // @ts-ignore + const phoneNumbersToContacts = createPhoneNumberToContactMapping(filteredContacts) + + const phoneNumbersByAddress = await lookupPhoneNumbersOnAttestations(web3, phoneNumbersToContacts) + + return { + rawContacts, + phoneNumbersByAddress, + } +} + +export function getContactForAddress( + address: string, + rawContacts: ContactsById, + addressMapping: PhoneNumberMappingEntryByAddress +): Contact | undefined { + const entry = addressMapping[address] + + if (entry === undefined) { + return undefined + } + + const contact = rawContacts[entry.id] + if (contact === undefined) { + return undefined + } + + return contact +} diff --git a/packages/dappkit/tsconfig.json b/packages/dappkit/tsconfig.json index fb4a8f39203..757ae60d70f 100644 --- a/packages/dappkit/tsconfig.json +++ b/packages/dappkit/tsconfig.json @@ -5,7 +5,7 @@ "baseUrl": ".", "declaration": true, "jsx": "preserve", - "lib": ["dom", "es2015", "es2016"], + "lib": ["dom", "es2015", "es2016", "es2017", "es2018", "esnext"], "module": "commonjs", "moduleResolution": "node", "noUnusedLocals": true, @@ -13,7 +13,6 @@ "removeComments": false, "skipLibCheck": true, "sourceMap": true, - "target": "es5", "outDir": "lib", "downlevelIteration": true }, diff --git a/packages/docs/command-line-interface/account.md b/packages/docs/command-line-interface/account.md index 7ecfc883e14..eb14ca99d32 100644 --- a/packages/docs/command-line-interface/account.md +++ b/packages/docs/command-line-interface/account.md @@ -16,7 +16,7 @@ EXAMPLE balance 0x5409ed021d9299bf6814279a6a1411a7e866a631 ``` -_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)_ +_See code: [packages/cli/src/src/commands/account/balance.ts](https://github.com/celo-org/celo-monorepo/tree/master/packages/cli/src/src/commands/account/balance.ts)_ ### New @@ -30,7 +30,7 @@ EXAMPLE new ``` -_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)_ +_See code: [packages/cli/src/src/commands/account/new.ts](https://github.com/celo-org/celo-monorepo/tree/master/packages/cli/src/src/commands/account/new.ts)_ ### Transferdollar @@ -50,7 +50,7 @@ EXAMPLE --amountInWei 1 ``` -_See code: [packages/cli/src/commands/account/transferdollar.ts](https://github.com/celo-org/celo-monorepo/tree/master/packages/cli/src/commands/account/transferdollar.ts)_ +_See code: [packages/cli/src/src/commands/account/transferdollar.ts](https://github.com/celo-org/celo-monorepo/tree/master/packages/cli/src/src/commands/account/transferdollar.ts)_ ### Transfergold @@ -70,7 +70,7 @@ EXAMPLE --amountInWei 1 ``` -_See code: [packages/cli/src/commands/account/transfergold.ts](https://github.com/celo-org/celo-monorepo/tree/master/packages/cli/src/commands/account/transfergold.ts)_ +_See code: [packages/cli/src/src/commands/account/transfergold.ts](https://github.com/celo-org/celo-monorepo/tree/master/packages/cli/src/src/commands/account/transfergold.ts)_ ### Unlock @@ -88,4 +88,4 @@ EXAMPLE unlock --account 0x5409ed021d9299bf6814279a6a1411a7e866a631 --password 1234 ``` -_See code: [packages/cli/src/commands/account/unlock.ts](https://github.com/celo-org/celo-monorepo/tree/master/packages/cli/src/commands/account/unlock.ts)_ +_See code: [packages/cli/src/src/commands/account/unlock.ts](https://github.com/celo-org/celo-monorepo/tree/master/packages/cli/src/src/commands/account/unlock.ts)_ diff --git a/packages/docs/command-line-interface/bonds.md b/packages/docs/command-line-interface/bonds.md index 29dd3c0f708..3f4fc3b57c3 100644 --- a/packages/docs/command-line-interface/bonds.md +++ b/packages/docs/command-line-interface/bonds.md @@ -23,7 +23,7 @@ EXAMPLE deposit --from 0x47e172F6CfB6c7D01C1574fa3E2Be7CC73269D95 --noticePeriod 8640 --goldAmount 1000000000000000000 ``` -_See code: [packages/cli/src/commands/bonds/deposit.ts](https://github.com/celo-org/celo-monorepo/tree/master/packages/cli/src/commands/bonds/deposit.ts)_ +_See code: [packages/cli/src/src/commands/bonds/deposit.ts](https://github.com/celo-org/celo-monorepo/tree/master/packages/cli/src/src/commands/bonds/deposit.ts)_ ### List @@ -37,7 +37,7 @@ EXAMPLE list 0x5409ed021d9299bf6814279a6a1411a7e866a631 ``` -_See code: [packages/cli/src/commands/bonds/list.ts](https://github.com/celo-org/celo-monorepo/tree/master/packages/cli/src/commands/bonds/list.ts)_ +_See code: [packages/cli/src/src/commands/bonds/list.ts](https://github.com/celo-org/celo-monorepo/tree/master/packages/cli/src/src/commands/bonds/list.ts)_ ### Notify @@ -58,7 +58,7 @@ EXAMPLE notify --noticePeriod=3600 --goldAmount=500 ``` -_See code: [packages/cli/src/commands/bonds/notify.ts](https://github.com/celo-org/celo-monorepo/tree/master/packages/cli/src/commands/bonds/notify.ts)_ +_See code: [packages/cli/src/src/commands/bonds/notify.ts](https://github.com/celo-org/celo-monorepo/tree/master/packages/cli/src/src/commands/bonds/notify.ts)_ ### Register @@ -75,7 +75,7 @@ EXAMPLE register ``` -_See code: [packages/cli/src/commands/bonds/register.ts](https://github.com/celo-org/celo-monorepo/tree/master/packages/cli/src/commands/bonds/register.ts)_ +_See code: [packages/cli/src/src/commands/bonds/register.ts](https://github.com/celo-org/celo-monorepo/tree/master/packages/cli/src/src/commands/bonds/register.ts)_ ### Rewards @@ -95,7 +95,7 @@ EXAMPLES rewards --delegate=0x56e172F6CfB6c7D01C1574fa3E2Be7CC73269D95 ``` -_See code: [packages/cli/src/commands/bonds/rewards.ts](https://github.com/celo-org/celo-monorepo/tree/master/packages/cli/src/commands/bonds/rewards.ts)_ +_See code: [packages/cli/src/src/commands/bonds/rewards.ts](https://github.com/celo-org/celo-monorepo/tree/master/packages/cli/src/src/commands/bonds/rewards.ts)_ ### Show @@ -116,7 +116,7 @@ EXAMPLES show 0x5409ed021d9299bf6814279a6a1411a7e866a631 --availabilityTime=1562206887 ``` -_See code: [packages/cli/src/commands/bonds/show.ts](https://github.com/celo-org/celo-monorepo/tree/master/packages/cli/src/commands/bonds/show.ts)_ +_See code: [packages/cli/src/src/commands/bonds/show.ts](https://github.com/celo-org/celo-monorepo/tree/master/packages/cli/src/src/commands/bonds/show.ts)_ ### Withdraw @@ -136,4 +136,4 @@ EXAMPLE withdraw 3600 ``` -_See code: [packages/cli/src/commands/bonds/withdraw.ts](https://github.com/celo-org/celo-monorepo/tree/master/packages/cli/src/commands/bonds/withdraw.ts)_ +_See code: [packages/cli/src/src/commands/bonds/withdraw.ts](https://github.com/celo-org/celo-monorepo/tree/master/packages/cli/src/src/commands/bonds/withdraw.ts)_ diff --git a/packages/docs/command-line-interface/config.md b/packages/docs/command-line-interface/config.md index f313c741e4e..2c67580e498 100644 --- a/packages/docs/command-line-interface/config.md +++ b/packages/docs/command-line-interface/config.md @@ -13,7 +13,7 @@ USAGE $ celocli config:get ``` -_See code: [packages/cli/src/commands/config/get.ts](https://github.com/celo-org/celo-monorepo/tree/master/packages/cli/src/commands/config/get.ts)_ +_See code: [packages/cli/src/src/commands/config/get.ts](https://github.com/celo-org/celo-monorepo/tree/master/packages/cli/src/src/commands/config/get.ts)_ ### Set @@ -27,4 +27,4 @@ OPTIONS --node=node (required) [default: ws://localhost:8546] Node URL ``` -_See code: [packages/cli/src/commands/config/set.ts](https://github.com/celo-org/celo-monorepo/tree/master/packages/cli/src/commands/config/set.ts)_ +_See code: [packages/cli/src/src/commands/config/set.ts](https://github.com/celo-org/celo-monorepo/tree/master/packages/cli/src/src/commands/config/set.ts)_ diff --git a/packages/docs/command-line-interface/exchange.md b/packages/docs/command-line-interface/exchange.md index 16fc35f9776..4d4f9619427 100644 --- a/packages/docs/command-line-interface/exchange.md +++ b/packages/docs/command-line-interface/exchange.md @@ -19,7 +19,7 @@ EXAMPLE list ``` -_See code: [packages/cli/src/commands/exchange/list.ts](https://github.com/celo-org/celo-monorepo/tree/master/packages/cli/src/commands/exchange/list.ts)_ +_See code: [packages/cli/src/src/commands/exchange/list.ts](https://github.com/celo-org/celo-monorepo/tree/master/packages/cli/src/src/commands/exchange/list.ts)_ ### Selldollar @@ -38,7 +38,7 @@ EXAMPLE selldollar 100 300 0xc1912fEE45d61C87Cc5EA59DaE31190FFFFf232d ``` -_See code: [packages/cli/src/commands/exchange/selldollar.ts](https://github.com/celo-org/celo-monorepo/tree/master/packages/cli/src/commands/exchange/selldollar.ts)_ +_See code: [packages/cli/src/src/commands/exchange/selldollar.ts](https://github.com/celo-org/celo-monorepo/tree/master/packages/cli/src/src/commands/exchange/selldollar.ts)_ ### Sellgold @@ -57,4 +57,4 @@ EXAMPLE sellgold 100 300 0xc1912fEE45d61C87Cc5EA59DaE31190FFFFf232d ``` -_See code: [packages/cli/src/commands/exchange/sellgold.ts](https://github.com/celo-org/celo-monorepo/tree/master/packages/cli/src/commands/exchange/sellgold.ts)_ +_See code: [packages/cli/src/src/commands/exchange/sellgold.ts](https://github.com/celo-org/celo-monorepo/tree/master/packages/cli/src/src/commands/exchange/sellgold.ts)_ diff --git a/packages/docs/command-line-interface/node.md b/packages/docs/command-line-interface/node.md index f4c73dc394b..33cbd42f6f8 100644 --- a/packages/docs/command-line-interface/node.md +++ b/packages/docs/command-line-interface/node.md @@ -13,4 +13,4 @@ USAGE $ celocli node:accounts ``` -_See code: [packages/cli/src/commands/node/accounts.ts](https://github.com/celo-org/celo-monorepo/tree/master/packages/cli/src/commands/node/accounts.ts)_ +_See code: [packages/cli/src/src/commands/node/accounts.ts](https://github.com/celo-org/celo-monorepo/tree/master/packages/cli/src/src/commands/node/accounts.ts)_ diff --git a/packages/docs/command-line-interface/validator.md b/packages/docs/command-line-interface/validator.md index d42c4d4e561..e039dad8cd0 100644 --- a/packages/docs/command-line-interface/validator.md +++ b/packages/docs/command-line-interface/validator.md @@ -22,7 +22,7 @@ EXAMPLES affiliation --unset --from 0x47e172f6cfb6c7d01c1574fa3e2be7cc73269d95 ``` -_See code: [packages/cli/src/commands/validator/affiliation.ts](https://github.com/celo-org/celo-monorepo/tree/master/packages/cli/src/commands/validator/affiliation.ts)_ +_See code: [packages/cli/src/src/commands/validator/affiliation.ts](https://github.com/celo-org/celo-monorepo/tree/master/packages/cli/src/src/commands/validator/affiliation.ts)_ ### List @@ -36,7 +36,7 @@ EXAMPLE list ``` -_See code: [packages/cli/src/commands/validator/list.ts](https://github.com/celo-org/celo-monorepo/tree/master/packages/cli/src/commands/validator/list.ts)_ +_See code: [packages/cli/src/src/commands/validator/list.ts](https://github.com/celo-org/celo-monorepo/tree/master/packages/cli/src/src/commands/validator/list.ts)_ ### Register @@ -63,7 +63,7 @@ EXAMPLE 96bad27bb1c0fd6080a75b0ec9f75b50298a2a8e04b02b2688c8104fca61fb00 ``` -_See code: [packages/cli/src/commands/validator/register.ts](https://github.com/celo-org/celo-monorepo/tree/master/packages/cli/src/commands/validator/register.ts)_ +_See code: [packages/cli/src/src/commands/validator/register.ts](https://github.com/celo-org/celo-monorepo/tree/master/packages/cli/src/src/commands/validator/register.ts)_ ### Show @@ -80,4 +80,4 @@ EXAMPLE show 0x97f7333c51897469E8D98E7af8653aAb468050a3 ``` -_See code: [packages/cli/src/commands/validator/show.ts](https://github.com/celo-org/celo-monorepo/tree/master/packages/cli/src/commands/validator/show.ts)_ +_See code: [packages/cli/src/src/commands/validator/show.ts](https://github.com/celo-org/celo-monorepo/tree/master/packages/cli/src/src/commands/validator/show.ts)_ diff --git a/packages/docs/command-line-interface/validatorgroup.md b/packages/docs/command-line-interface/validatorgroup.md index 47670a39cbb..8c51463e3ec 100644 --- a/packages/docs/command-line-interface/validatorgroup.md +++ b/packages/docs/command-line-interface/validatorgroup.md @@ -16,7 +16,7 @@ EXAMPLE list ``` -_See code: [packages/cli/src/commands/validatorgroup/list.ts](https://github.com/celo-org/celo-monorepo/tree/master/packages/cli/src/commands/validatorgroup/list.ts)_ +_See code: [packages/cli/src/src/commands/validatorgroup/list.ts](https://github.com/celo-org/celo-monorepo/tree/master/packages/cli/src/src/commands/validatorgroup/list.ts)_ ### Member @@ -39,7 +39,7 @@ EXAMPLES member --remove 0x47e172f6cfb6c7d01c1574fa3e2be7cc73269d95 ``` -_See code: [packages/cli/src/commands/validatorgroup/member.ts](https://github.com/celo-org/celo-monorepo/tree/master/packages/cli/src/commands/validatorgroup/member.ts)_ +_See code: [packages/cli/src/src/commands/validatorgroup/member.ts](https://github.com/celo-org/celo-monorepo/tree/master/packages/cli/src/src/commands/validatorgroup/member.ts)_ ### Register @@ -61,7 +61,7 @@ EXAMPLE "http://vgroup.com" ``` -_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)_ +_See code: [packages/cli/src/src/commands/validatorgroup/register.ts](https://github.com/celo-org/celo-monorepo/tree/master/packages/cli/src/src/commands/validatorgroup/register.ts)_ ### Show @@ -78,7 +78,7 @@ EXAMPLE show 0x97f7333c51897469E8D98E7af8653aAb468050a3 ``` -_See code: [packages/cli/src/commands/validatorgroup/show.ts](https://github.com/celo-org/celo-monorepo/tree/master/packages/cli/src/commands/validatorgroup/show.ts)_ +_See code: [packages/cli/src/src/commands/validatorgroup/show.ts](https://github.com/celo-org/celo-monorepo/tree/master/packages/cli/src/src/commands/validatorgroup/show.ts)_ ### Vote @@ -100,4 +100,4 @@ EXAMPLES vote --from 0x4443d0349e8b3075cba511a0a87796597602a0f1 --current ``` -_See code: [packages/cli/src/commands/validatorgroup/vote.ts](https://github.com/celo-org/celo-monorepo/tree/master/packages/cli/src/commands/validatorgroup/vote.ts)_ +_See code: [packages/cli/src/src/commands/validatorgroup/vote.ts](https://github.com/celo-org/celo-monorepo/tree/master/packages/cli/src/src/commands/validatorgroup/vote.ts)_ diff --git a/packages/helm-charts/load-test/templates/statefulset.yaml b/packages/helm-charts/load-test/templates/statefulset.yaml index 5274c9fcdc9..654fad995ca 100644 --- a/packages/helm-charts/load-test/templates/statefulset.yaml +++ b/packages/helm-charts/load-test/templates/statefulset.yaml @@ -76,7 +76,7 @@ spec: --wsorigins=* \ --wsapi=eth,net,web3,debug \ --networkid={{ .Values.networkID }} \ - --syncmode=celolatest \ + --syncmode=ultralight \ --consoleformat=json \ --consoleoutput=stdout \ --verbosity={{ .Values.geth.verbosity }}" diff --git a/packages/helm-charts/tracer-tool/scripts/run.sh b/packages/helm-charts/tracer-tool/scripts/run.sh index 9f24e59e4c8..acda36c5a6c 100644 --- a/packages/helm-charts/tracer-tool/scripts/run.sh +++ b/packages/helm-charts/tracer-tool/scripts/run.sh @@ -22,7 +22,7 @@ cat /root/staticnodes >> $DATA_DIR/static-nodes.json; echo "Running geth..."; -$CELOTOOL geth run --geth-dir $GETH_DIR --data-dir $DATA_DIR --sync-mode celolatest --verbosity 1 & +$CELOTOOL geth run --geth-dir $GETH_DIR --data-dir $DATA_DIR --sync-mode ultralight --verbosity 1 & sleep 15; diff --git a/packages/mobile/android/app/build.gradle b/packages/mobile/android/app/build.gradle index 39a6807f1ae..7bffc6fb76c 100644 --- a/packages/mobile/android/app/build.gradle +++ b/packages/mobile/android/app/build.gradle @@ -122,7 +122,7 @@ android { minSdkVersion isDetoxTestBuild ? rootProject.ext.minSdkVersion : 18 targetSdkVersion rootProject.ext.targetSdkVersion versionCode appVersionCode - versionName "1.4.0" + versionName "1.4.1" testBuildType System.getProperty('testBuildType', 'debug') testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" resValue "string", "build_config_package", "org.celo.mobile" diff --git a/packages/mobile/android/gradle.properties b/packages/mobile/android/gradle.properties index 34b856f3154..47aa3c0a94c 100644 --- a/packages/mobile/android/gradle.properties +++ b/packages/mobile/android/gradle.properties @@ -20,4 +20,4 @@ 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=1004000 \ No newline at end of file +VERSION_CODE=100400100 \ No newline at end of file diff --git a/packages/mobile/ios/celo-tvOS/Info.plist b/packages/mobile/ios/celo-tvOS/Info.plist index 0e28d433a13..d526899a060 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.0 + 1.4.1 CFBundleSignature ???? CFBundleVersion - 5 + 6 LSRequiresIPhoneOS UILaunchStoryboardName diff --git a/packages/mobile/ios/celo-tvOSTests/Info.plist b/packages/mobile/ios/celo-tvOSTests/Info.plist index 85e889ee899..5676fee33e2 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.0 + 1.4.1 CFBundleSignature ???? CFBundleVersion - 5 + 6 diff --git a/packages/mobile/ios/celo/Info.plist b/packages/mobile/ios/celo/Info.plist index 7ed93a16b16..8d9352c1b6a 100644 --- a/packages/mobile/ios/celo/Info.plist +++ b/packages/mobile/ios/celo/Info.plist @@ -17,11 +17,11 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.4.0 + 1.4.1 CFBundleSignature ???? CFBundleVersion - 5 + 6 LSRequiresIPhoneOS UILaunchStoryboardName diff --git a/packages/mobile/ios/celoTests/Info.plist b/packages/mobile/ios/celoTests/Info.plist index 85e889ee899..5676fee33e2 100644 --- a/packages/mobile/ios/celoTests/Info.plist +++ b/packages/mobile/ios/celoTests/Info.plist @@ -15,10 +15,10 @@ CFBundlePackageType BNDL CFBundleShortVersionString - 1.4.0 + 1.4.1 CFBundleSignature ???? CFBundleVersion - 5 + 6 diff --git a/packages/mobile/locales/en-US/dappkit.json b/packages/mobile/locales/en-US/dappkit.json new file mode 100644 index 00000000000..a8d803e27da --- /dev/null +++ b/packages/mobile/locales/en-US/dappkit.json @@ -0,0 +1,15 @@ +{ + "connectToWallet": "Savings Circle would like to connect to Celo Wallet", + "connect": "Connect", + "cancel": "Cancel", + "allow": "Allow", + "address": "Address", + "transaction": { + "operation": "Operation", + "data": "Data", + "signTX": "Sign TX", + "details": "Show details" + }, + "shareInfo": "Share the following info:", + "data": "Data" +} diff --git a/packages/mobile/locales/en-US/exchangeFlow9.json b/packages/mobile/locales/en-US/exchangeFlow9.json index cbec2f11e06..3710a2418b3 100644 --- a/packages/mobile/locales/en-US/exchangeFlow9.json +++ b/packages/mobile/locales/en-US/exchangeFlow9.json @@ -1,7 +1,5 @@ { "learnMore": "Learn More", - "celoDollars": "Celo Dollars", - "celoGold": "Celo Gold", "whatIsGold": "Gold is where you can choose to store Celo dollars you have", "manageCelo": "Manage Celo Dollars", "activity": "Activity", diff --git a/packages/mobile/locales/en-US/index.ts b/packages/mobile/locales/en-US/index.ts index b54e4dd32af..aef6a188502 100644 --- a/packages/mobile/locales/en-US/index.ts +++ b/packages/mobile/locales/en-US/index.ts @@ -1,5 +1,6 @@ import accountScreen10 from '@celo/mobile/locales/en-US/accountScreen10.json' import backupKeyFlow6 from '@celo/mobile/locales/en-US/backupKeyFlow6.json' +import dappkit from '@celo/mobile/locales/en-US/dappkit.json' import dev from '@celo/mobile/locales/en-US/dev.json' import exchangeFlow9 from '@celo/mobile/locales/en-US/exchangeFlow9.json' import global from '@celo/mobile/locales/en-US/global.json' @@ -28,4 +29,5 @@ export default { sendFlow7, walletFlow5, dev, + dappkit, } diff --git a/packages/mobile/locales/en-US/nuxCurrencyPhoto4.json b/packages/mobile/locales/en-US/nuxCurrencyPhoto4.json index 5cd990a4361..a4cd716239c 100644 --- a/packages/mobile/locales/en-US/nuxCurrencyPhoto4.json +++ b/packages/mobile/locales/en-US/nuxCurrencyPhoto4.json @@ -1,6 +1,4 @@ { - "celoDollars": "Celo Dollars", - "celoGold": "Celo Gold", "getStarted": "Get Started", "getBackupKey": "Get Backup Key", "setYourBackupKey": @@ -10,7 +8,6 @@ "stableDollar": "Celo Dollar is a stable digital asset that tracks to the value of the US Dollar", "feeTransaction": "Sending transactions cost a small fee", "sendCelo": "Send Celo Dollars to anyone with a mobile phone", - "sendCeloDollars": "Send Celo Dollars", "backToWallet": "Back to Wallet", "celoLikeGold": "Celo Gold is like real gold", "goldFluctuates": "There’s a limited amount of Celo Gold and the value can go up and down", diff --git a/packages/mobile/locales/en-US/sendFlow7.json b/packages/mobile/locales/en-US/sendFlow7.json index 21e8a3e552d..97b262f7b3c 100644 --- a/packages/mobile/locales/en-US/sendFlow7.json +++ b/packages/mobile/locales/en-US/sendFlow7.json @@ -54,7 +54,7 @@ "Storage write permission is needed for downloading the QR code", "ScanCodeByPlacingItInTheBox": "Scan code by placing it in the box", "needCameraPermissionToScan": "App needs camera permission to scan QR codes", - "backToYourQRCode": "Back to your QR Code", + "showYourQRCode": "Show your QR code", "toSentOrRequestPayment": "to send or request payment", "requestSent": "Request Sent", "reclaimPayment": "Reclaim Payment", diff --git a/packages/mobile/locales/en-US/walletFlow5.json b/packages/mobile/locales/en-US/walletFlow5.json index 26ccffd98b1..8d8b8d0cfb2 100644 --- a/packages/mobile/locales/en-US/walletFlow5.json +++ b/packages/mobile/locales/en-US/walletFlow5.json @@ -1,6 +1,4 @@ { - "celoDollars": "Celo Dollars", - "celoGold": "Celo Gold", "getStarted": "Get Started", "paymentRequest": "Payment Request", "paymentRequestWithCount_plural": "{{count}} Payment Requests", @@ -47,7 +45,6 @@ "0": "Unable to load your feed", "1": "Please try again later" }, - "sendCeloDollars": "Send Celo Dollars", "noTransactionActivity": "You will see your sent and received payments here", "noExchangeActivity": "You will see completed exchanges here", "maybeLater": "Maybe Later", diff --git a/packages/mobile/locales/en.json b/packages/mobile/locales/en.json index 88b8269c829..8565d5fdd28 100644 --- a/packages/mobile/locales/en.json +++ b/packages/mobile/locales/en.json @@ -92,8 +92,6 @@ "fee": "Fee" }, "faq_terms_footer": "<0>Test FAQ is <1>here<2> Terms of service are <3>here", - "celoDollars": "Celo Dollars", - "celoGold": "Celo Gold", "invalidInvitation": "Invalid Invitation Code ", "incorrectPin": "Incorrect PIN", "photosNUX": { diff --git a/packages/mobile/locales/es-AR/dappkit.json b/packages/mobile/locales/es-AR/dappkit.json new file mode 100644 index 00000000000..106fb8340c6 --- /dev/null +++ b/packages/mobile/locales/es-AR/dappkit.json @@ -0,0 +1,15 @@ +{ + "connectToWallet": "Círculo de Ahorro le gustaría conectarse a Celo Monedero", + "connect": "Conectar", + "cancel": "Cancelar", + "allow": "Permitir", + "address": "Dirección", + "transaction": { + "operation": "Operación", + "data": "Datos", + "signTX": "Boleto TX", + "details": "Mostrar detalles" + }, + "shareInfo": "Comparte la siguiente información:", + "data": "Datos" +} diff --git a/packages/mobile/locales/es-AR/exchangeFlow9.json b/packages/mobile/locales/es-AR/exchangeFlow9.json index 72385eb07f3..6f1832f98d0 100755 --- a/packages/mobile/locales/es-AR/exchangeFlow9.json +++ b/packages/mobile/locales/es-AR/exchangeFlow9.json @@ -1,7 +1,5 @@ { "learnMore": "Saber más", - "celoDollars": "Celo Dólares", - "celoGold": "Celo Oro", "whatIsGold": "Puede elegir guardar sus Celo Dólares en Celo Oro", "manageCelo": "Administrar Celo Dólares", "activity": "Actividad", diff --git a/packages/mobile/locales/es-AR/nuxCurrencyPhoto4.json b/packages/mobile/locales/es-AR/nuxCurrencyPhoto4.json index 4c8e541df89..31dfb98e5b5 100755 --- a/packages/mobile/locales/es-AR/nuxCurrencyPhoto4.json +++ b/packages/mobile/locales/es-AR/nuxCurrencyPhoto4.json @@ -1,6 +1,4 @@ { - "celoDollars": "Celo Dólares", - "celoGold": "Celo Oro", "getStarted": "Primeros pasos", "getBackupKey": "Obtener clave de respaldo", "setYourBackupKey": @@ -11,7 +9,6 @@ "El Celo Dólar constituye un activo digital estable que está alineado con el valor del dólar estadounidense", "feeTransaction": "El envío de transacciones tiene una pequeña comisión", "sendCelo": "Enviar Celo Dólares a cualquier persona con un celular", - "sendCeloDollars": "Enviar Celo Dólares", "backToWallet": "Volver a el Monedero", "celoLikeGold": "Celo Oro es como el oro real", "goldFluctuates": "Hay una cantidad limitada de Celo Oro, y su valor puede aumentar o disminuir", diff --git a/packages/mobile/locales/es-AR/nuxNamePin1.json b/packages/mobile/locales/es-AR/nuxNamePin1.json index ce3cc1af68b..01cb5293136 100755 --- a/packages/mobile/locales/es-AR/nuxNamePin1.json +++ b/packages/mobile/locales/es-AR/nuxNamePin1.json @@ -9,26 +9,26 @@ "verifyNumber": "Verifique su número de teléfono para conectarse con otros usuarios y recibir dinero", "syncNetwork": "Sincronizando con la red", - "welcomeCelo": "Le damos la bienvenida a Celo", + "welcomeCelo": "Te damos la bienvenida a Celo", "chooseCountryCode": "Código de país", "joinText": { "0": - "Bienvenido a Celo Wallet, un monedero digital que le permite enviar, recibir y guardar valor fácilmente.", + "Bienvenido a Celo Wallet, una billetera digital que te permite enviar, recibir y guardar valor fácilmente.", "1": - "Al unirte a esta red, nos das permiso para recopilar información anónima sobre cómo utilizas la aplicación. Además, si verificas tu número de teléfono, se almacenará una copia ofuscada en la Red de Celo. Más información en", + "Al unirte a esta red, nos das permiso para recopilar información anónima sobre cómo utilizas la aplicación. Adrmás, si verificas tu número de teléfono, se almacenará una copia ofuscada en la Red de Celo. Más información en ", "2": "celo.org/terms" }, "phoneNumber": "000 000 0000", "inviteCodeText": { - "title": "Ingrese el código de invitación", + "title": "Ingresa el código de invitación", "copyInvite": { - "0": "Copia de invitación ", + "0": "Copia el código de invitación ", "1": "desde la aplicación de mensajes y vuelve." }, "openMessages": { "message": "Abrir mensajes", "hint": { - "0": "Insinuación ", + "0": "Pista ", "1": "Copia el mensaje SMS completo" } }, @@ -42,9 +42,9 @@ }, "inviteAccepted": "🎉 Invitación aceptada!", "askForInvite": { - "0": "¿No tiene un código? ", + "0": "¿No tienes un código? ", "1": - "Solicite una invitación a alguien con un Monedero Celo o regístrese para obtener una cuenta de testnet en", + "Solicita una invitación a alguien con la Celo Wallet o regístrate para obtener una cuenta de testnet en", "2": "celo.org/build/wallet" } }, @@ -53,8 +53,8 @@ "InvitationCode": "Código de invitación", "optIn": "Inscribirse", "submitting": "Enviando ...", - "haveWallet": "¿Ya tiene un Monedero Celo? ", - "importIt": "Importar", + "haveWallet": "¿Ya tienes una billetera de Celo? ", + "importIt": "Impórtala", "cancel": "Cancelar", "important": "Importante", "createPin": { diff --git a/packages/mobile/locales/es-AR/nuxRestoreWallet3.json b/packages/mobile/locales/es-AR/nuxRestoreWallet3.json index 100cd0f810a..f3bbdb5250c 100755 --- a/packages/mobile/locales/es-AR/nuxRestoreWallet3.json +++ b/packages/mobile/locales/es-AR/nuxRestoreWallet3.json @@ -1,5 +1,5 @@ { - "welcomeCelo": "Le damos la bienvenida a Celo", + "welcomeCelo": "Te damos la bienvenida a Celo", "enterInvite": "Ingrese su código de invitación. Si no tiene uno, pídale a alguien de la comunidad de Celo que lo invite.\n\nAl unirse a esta aplicación, acuerda compartir su nombre y número telefónico con nosotros. Obtenga más información en celo.org", "fullName": "Nombre completo", diff --git a/packages/mobile/locales/es-AR/sendFlow7.json b/packages/mobile/locales/es-AR/sendFlow7.json index bc3f4ee0b68..290f117fa73 100755 --- a/packages/mobile/locales/es-AR/sendFlow7.json +++ b/packages/mobile/locales/es-AR/sendFlow7.json @@ -55,7 +55,7 @@ "ScanCodeByPlacingItInTheBox": "Escanee el código colocándolo en la caja", "needCameraPermissionToScan": "La aplicación necesita permiso de la cámara para escanear códigos QR", - "backToYourQRCode": "Volver a su código QR", + "showYourQRCode": "Muestra su código QR", "toSentOrRequestPayment": "enviar o solicitar pago", "requestSent": "Solicitud Enviada", "reclaimPayment": "Reclamar el Pago", diff --git a/packages/mobile/locales/es-AR/walletFlow5.json b/packages/mobile/locales/es-AR/walletFlow5.json index 8e8ffb67544..0276b38476e 100755 --- a/packages/mobile/locales/es-AR/walletFlow5.json +++ b/packages/mobile/locales/es-AR/walletFlow5.json @@ -1,6 +1,4 @@ { - "celoDollars": "Celo Dólares", - "celoGold": "Celo Oro", "getStarted": "Primeros pasos", "paymentRequest": "Solicitud de pago", "paymentRequestWithCount_plural": "{{count}} solicitudes de pago", @@ -48,7 +46,6 @@ "0": "No se puede cargar tu actividad", "1": "Por favor inténtalo de nuevo más tarde" }, - "sendCeloDollars": "Enviar Celo Dólares", "noTransactionActivity": "Verá sus pagos enviados y recibidos aquí", "noExchangeActivity": "Verá intercambios completados aquí", "maybeLater": "quizas mas tarde", diff --git a/packages/mobile/package.json b/packages/mobile/package.json index 595f61e39fe..106b3158df0 100644 --- a/packages/mobile/package.json +++ b/packages/mobile/package.json @@ -1,6 +1,6 @@ { "name": "@celo/mobile", - "version": "1.4.0", + "version": "1.4.1", "author": "Celo", "license": "Apache-2.0", "private": true, @@ -45,9 +45,9 @@ }, "dependencies": { "@celo/client": "f7095b7", - "@celo/walletkit": "^0.0.4", "@celo/react-native-sms-retriever": "git+https://github.com/celo-org/react-native-sms-retriever#d3a2fdb", - "@celo/utils": "^0.0.5", + "@celo/utils": "^0.0.6-beta5", + "@celo/walletkit": "^0.0.14", "@react-native-community/netinfo": "^2.0.4", "@segment/analytics-react-native": "^0.1.0-beta.0", "@segment/analytics-react-native-firebase": "^0.1.0-beta.0", @@ -143,7 +143,7 @@ "@types/graphql": "^14.0.7", "@types/hoist-non-react-statics": "^3.0.1", "@types/isomorphic-fetch": "^0.0.35", - "@types/jest": "^24.0.13", + "@types/jest": "^24.0.17", "@types/lodash": "^4.14.136", "@types/react": "^16.8.19", "@types/react-native": "^0.57.47", @@ -181,7 +181,6 @@ "redux-saga-test-plan": "^4.0.0-beta.2", "remote-redux-devtools": "^0.5.12", "ts-jest": "^24.0.0", - "ts-node": "^7.0.1", "typescript": "^3.3.3" }, "detox": { diff --git a/packages/mobile/src/account/DollarEducation.tsx b/packages/mobile/src/account/DollarEducation.tsx index 067ba67b3f9..bd5b0b66848 100644 --- a/packages/mobile/src/account/DollarEducation.tsx +++ b/packages/mobile/src/account/DollarEducation.tsx @@ -56,7 +56,7 @@ export class DollarEducation extends React.Component { stepInfo={stepInfo} onFinish={this.goToSend} onFinishAlternate={this.goToWalletHome} - buttonText={'sendCeloDollars'} + buttonText={'global:sendCeloDollars'} linkText={'backToWallet'} /> ) diff --git a/packages/mobile/src/components/AccountOverview.tsx b/packages/mobile/src/components/AccountOverview.tsx index 443658bef0f..a11604f0f94 100644 --- a/packages/mobile/src/components/AccountOverview.tsx +++ b/packages/mobile/src/components/AccountOverview.tsx @@ -81,7 +81,7 @@ export class AccountOverview extends React.Component { - {t('celoDollars') + ' ' + CURRENCIES[Tokens.DOLLAR].code} + {t('global:celoDollars') + ' ' + CURRENCIES[Tokens.DOLLAR].code} { - {t('celoGold') + ' ' + CURRENCIES[Tokens.GOLD].code} + {t('global:celoGold') + ' ' + CURRENCIES[Tokens.GOLD].code} { } render() { - const { size } = this.props + const { size, type } = this.props const fontSize = size const dollarStyle = { fontSize, lineHeight: Math.round(fontSize * 1.3), color: this.color() } + const currencySymbol = CURRENCIES[type].symbol return ( - {this.props.type === CURRENCY_ENUM.DOLLAR ? ( - - $ - - ) : null} + + {currencySymbol} + {this.amount()} diff --git a/packages/mobile/src/components/__snapshots__/AccountOverview.test.tsx.snap b/packages/mobile/src/components/__snapshots__/AccountOverview.test.tsx.snap index d0efce52b6c..5b3edfbf978 100644 --- a/packages/mobile/src/components/__snapshots__/AccountOverview.test.tsx.snap +++ b/packages/mobile/src/components/__snapshots__/AccountOverview.test.tsx.snap @@ -47,7 +47,7 @@ exports[`renders correctly when Dollar education NUX flow hasn't been completed ] } > - celoDollars cUSD + global:celoDollars cUSD - celoGold cGLD + global:celoGold cGLD + + + - celoDollars cUSD + global:celoDollars cUSD - celoGold cGLD + global:celoGold cGLD + + + - celoDollars cUSD + global:celoDollars cUSD - celoGold cGLD + global:celoGold cGLD + + + - celoDollars cUSD + global:celoDollars cUSD - celoGold cGLD + global:celoGold cGLD + + + - celoDollars cUSD + global:celoDollars cUSD - celoGold cGLD + global:celoGold cGLD + + + { navigateHome({ dispatchAfterNavigate: approveAccountAuth(request) }) } + cancel = () => { + navigateBack() + } + render() { + const { t, account } = this.props return ( - - - - {this.props.account} - + + + + + + {t('connectToWallet')} + + {t('shareInfo')} + + + {t('address')} + {account} + + + + +