├── .circleci ├── README.md ├── config.yml └── images │ └── build │ └── Dockerfile ├── .editorconfig ├── .gitattributes ├── .github ├── CODEOWNERS ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE.md ├── PULL_REQUEST_TEMPLATE.md └── SUPPORT.md ├── .gitignore ├── .nvmrc ├── .soliumrc.json ├── .yarnrc ├── CODE_OF_CONDUCT.md ├── LICENSE ├── README.md ├── bin ├── README.md ├── compile ├── detect └── release ├── e2e-prepare.sh ├── e2e-run.sh ├── jest.config.js ├── lerna.json ├── logo.svg ├── package.json ├── packages ├── apps │ ├── .env.example │ ├── .gitignore │ ├── .solcover.js │ ├── .soliumignore │ ├── .soliumrc.json │ ├── README.md │ ├── contracts │ │ ├── HighRollerApp.sol │ │ ├── Migrations.sol │ │ ├── NimApp.sol │ │ ├── SimpleTransferApp.sol │ │ ├── SimpleTwoPartySwapApp.sol │ │ ├── TicTacToeApp.sol │ │ ├── UnidirectionalLinkedTransferApp.sol │ │ └── UnidirectionalTransferApp.sol │ ├── expected-build-artifacts │ │ ├── CounterfactualApp.json │ │ ├── HighRollerApp.json │ │ ├── LibOutcome.json │ │ ├── Migrations.json │ │ ├── NimApp.json │ │ ├── SafeMath.json │ │ ├── SimpleTransferApp.json │ │ ├── SimpleTwoPartySwapApp.json │ │ ├── TicTacToeApp.json │ │ ├── UnidirectionalLinkedTransferApp.json │ │ └── UnidirectionalTransferApp.json │ ├── migrations │ │ ├── 1_initial_migration.js │ │ └── 2_deploy_contracts.js │ ├── networks │ │ ├── 3.json │ │ ├── 4.json │ │ └── 42.json │ ├── package.json │ ├── test │ │ ├── high-roller-app.spec.ts │ │ ├── linked-transfer-app.spec.ts │ │ ├── nim.spec.ts │ │ ├── simple-swap-app.spec.ts │ │ ├── simple-transfer-app.spec.ts │ │ ├── tictactoe.spec.ts │ │ └── unidirectional-transfer-app.spec.ts │ ├── truffle-config.js │ ├── tsconfig.json │ ├── tslint.json │ └── waffle.js ├── cf-adjudicator-contracts │ ├── .env.example │ ├── .gitignore │ ├── .solcover.js │ ├── .soliumignore │ ├── .soliumrc.json │ ├── README.md │ ├── contracts │ │ ├── ChallengeRegistry.sol │ │ ├── Migrations.sol │ │ ├── interfaces │ │ │ └── CounterfactualApp.sol │ │ ├── libs │ │ │ ├── LibAppCaller.sol │ │ │ └── LibStateChannelApp.sol │ │ ├── mixins │ │ │ ├── MChallengeRegistryCore.sol │ │ │ ├── MixinCancelChallenge.sol │ │ │ ├── MixinChallengeRegistryCore.sol │ │ │ ├── MixinRespondToChallenge.sol │ │ │ ├── MixinSetOutcome.sol │ │ │ ├── MixinSetState.sol │ │ │ └── MixinSetStateWithAction.sol │ │ └── test-fixtures │ │ │ └── AppWithAction.sol │ ├── expected-build-artifacts │ │ ├── AppWithAction.json │ │ ├── ChallengeRegistry.json │ │ ├── CounterfactualApp.json │ │ ├── ECDSA.json │ │ ├── LibAppCaller.json │ │ ├── LibStateChannelApp.json │ │ ├── MChallengeRegistryCore.json │ │ ├── Migrations.json │ │ ├── MixinCancelChallenge.json │ │ ├── MixinChallengeRegistryCore.json │ │ ├── MixinRespondToChallenge.json │ │ ├── MixinSetOutcome.json │ │ ├── MixinSetState.json │ │ └── MixinSetStateWithAction.json │ ├── migrations │ │ ├── 1_initial_migration.js │ │ └── 2_deploy_contracts.js │ ├── networks │ │ ├── 1.json │ │ ├── 3.json │ │ ├── 4.json │ │ └── 42.json │ ├── package.json │ ├── test │ │ ├── challenge-registry-dispute.spec.ts │ │ ├── challenge-registry.spec.ts │ │ ├── check-deployed-contracts.spec.ts │ │ └── utils │ │ │ └── index.ts │ ├── truffle-config.js │ ├── tsconfig.json │ ├── tslint.json │ └── waffle.js ├── cf-funding-protocol-contracts │ ├── .env.example │ ├── .gitignore │ ├── .soliumignore │ ├── .soliumrc.json │ ├── README.md │ ├── contracts │ │ ├── ConditionalTransactionDelegateTarget.sol │ │ ├── Interpreter.sol │ │ ├── Migrations.sol │ │ ├── default-apps │ │ │ ├── CoinBalanceRefundApp.sol │ │ │ ├── FinalizedApp.sol │ │ │ ├── IdentityApp.sol │ │ │ └── TimeLockedPassthrough.sol │ │ ├── interpreters │ │ │ ├── MultiAssetMultiPartyCoinTransferFromVirtualAppInterpreter.sol │ │ │ ├── MultiAssetMultiPartyCoinTransferInterpreter.sol │ │ │ ├── SingleAssetTwoPartyCoinTransferFromVirtualAppInterpreter.sol │ │ │ ├── SingleAssetTwoPartyCoinTransferInterpreter.sol │ │ │ ├── TwoPartyFixedOutcomeFromVirtualAppInterpreter.sol │ │ │ └── TwoPartyFixedOutcomeInterpreter.sol │ │ ├── libs │ │ │ └── LibOutcome.sol │ │ ├── proxies │ │ │ ├── Proxy.sol │ │ │ └── ProxyFactory.sol │ │ ├── state-deposit-holders │ │ │ └── MinimumViableMultisig.sol │ │ └── test-fixtures │ │ │ ├── DelegateProxy.sol │ │ │ ├── DolphinCoin.sol │ │ │ ├── Echo.sol │ │ │ └── FixedTwoPartyOutcomeApp.sol │ ├── expected-build-artifacts │ │ ├── ChallengeRegistry.json │ │ ├── CoinBalanceRefundApp.json │ │ ├── ConditionalTransactionDelegateTarget.json │ │ ├── CounterfactualApp.json │ │ ├── DelegateProxy.json │ │ ├── DolphinCoin.json │ │ ├── ECDSA.json │ │ ├── ERC20.json │ │ ├── Echo.json │ │ ├── FinalizedApp.json │ │ ├── IERC20.json │ │ ├── IdentityApp.json │ │ ├── Interpreter.json │ │ ├── LibAppCaller.json │ │ ├── LibOutcome.json │ │ ├── LibStateChannelApp.json │ │ ├── MChallengeRegistryCore.json │ │ ├── Migrations.json │ │ ├── MinimumViableMultisig.json │ │ ├── MixinCancelChallenge.json │ │ ├── MixinChallengeRegistryCore.json │ │ ├── MixinRespondToChallenge.json │ │ ├── MixinSetOutcome.json │ │ ├── MixinSetState.json │ │ ├── MixinSetStateWithAction.json │ │ ├── MultiAssetMultiPartyCoinTransferFromVirtualAppInterpreter.json │ │ ├── MultiAssetMultiPartyCoinTransferInterpreter.json │ │ ├── Proxy.json │ │ ├── ProxyFactory.json │ │ ├── SafeMath.json │ │ ├── SingleAssetTwoPartyCoinTransferFromVirtualAppInterpreter.json │ │ ├── SingleAssetTwoPartyCoinTransferInterpreter.json │ │ ├── TimeLockedPassThrough.json │ │ ├── TwoPartyFixedOutcomeApp.json │ │ ├── TwoPartyFixedOutcomeFromVirtualAppInterpreter.json │ │ └── TwoPartyFixedOutcomeInterpreter.json │ ├── manual-deploy.js │ ├── migrations │ │ ├── 1_initial_migration.js │ │ └── 2_deploy_contracts.js │ ├── networks │ │ ├── 1.json │ │ ├── 3.json │ │ ├── 4.json │ │ └── 42.json │ ├── package.json │ ├── test │ │ ├── coin-transfer-interpreter.spec.ts │ │ ├── create2.spec.ts │ │ ├── erc20-example.spec.ts │ │ ├── single-asset-two-party-coin-transfer-from-virtual-app-interpreter.spec.ts │ │ └── utils │ │ │ └── index.ts │ ├── truffle-config.js │ ├── tsconfig.json │ ├── tslint.json │ └── waffle.js ├── cf-metamask-extension │ └── README.md ├── cf-wallet.js │ ├── .gitignore │ ├── README.md │ ├── jest.config.js │ ├── package.json │ ├── rollup.config.js │ ├── src │ │ ├── app-instance.ts │ │ ├── index.ts │ │ ├── provider.ts │ │ ├── types │ │ │ ├── events.ts │ │ │ └── index.ts │ │ └── utils │ │ │ ├── abi.ts │ │ │ └── index.ts │ ├── test │ │ ├── fixture.ts │ │ ├── provider.spec.ts │ │ └── utils │ │ │ └── abi.spec.ts │ ├── tsconfig.json │ ├── tslint.json │ └── typedoc.json ├── cf.js │ ├── .gitignore │ ├── README.md │ ├── jest.config.js │ ├── package.json │ ├── rollup.config.js │ ├── src │ │ ├── app-factory.ts │ │ ├── app-instance.ts │ │ ├── index.ts │ │ ├── provider.ts │ │ ├── types │ │ │ ├── events.ts │ │ │ └── index.ts │ │ └── utils │ │ │ ├── abi.ts │ │ │ └── index.ts │ ├── test │ │ ├── app-factory.spec.ts │ │ ├── app-instance.spec.ts │ │ ├── fixture.ts │ │ ├── provider.spec.ts │ │ └── utils │ │ │ └── abi.spec.ts │ ├── tsconfig.json │ ├── tslint.json │ └── typedoc.json ├── firebase-client │ ├── package.json │ ├── rollup.config.js │ ├── src │ │ └── index.ts │ ├── tsconfig.json │ └── tslint.json ├── firebase-server │ ├── .env.defaults │ ├── .env.example │ ├── .env.schema │ ├── .gitignore │ ├── jest.config.js │ ├── package.json │ ├── rollup.config.js │ ├── src │ │ └── index.ts │ ├── test │ │ └── integration │ │ │ └── store.spec.ts │ ├── tsconfig.json │ └── tslint.json ├── greenboard │ ├── .gitignore │ ├── README.md │ ├── package.json │ ├── tests │ │ ├── login.spec.ts │ │ └── onboarding.spec.ts │ ├── tsconfig.json │ ├── tslint.json │ └── utils │ │ ├── chrome-selectors.ts │ │ ├── counterfactual-wallet-selectors.ts │ │ ├── metamask-selectors.ts │ │ ├── state-collector.ts │ │ ├── test-browser.ts │ │ ├── test-sequencer.js │ │ └── types.ts ├── local-ganache-server │ ├── .env.defaults │ ├── .gitignore │ ├── README.md │ ├── jest.config.js │ ├── package.json │ ├── src │ │ ├── contract-deployments.jest.ts │ │ └── index.ts │ ├── test │ │ └── contracts-deployment.spec.ts │ ├── tsconfig.json │ └── tslint.json ├── node-provider │ ├── .gitignore │ ├── jest.config.js │ ├── package.json │ ├── rollup.config.js │ ├── src │ │ ├── index.ts │ │ ├── node-provider-ethereum.ts │ │ ├── node-provider.ts │ │ └── types.ts │ ├── test │ │ ├── integration │ │ │ └── .gitkeep │ │ ├── unit │ │ │ ├── node-provider-ethereum.spec.ts │ │ │ └── node-provider.spec.ts │ │ └── utils │ │ │ ├── ethereum-mock.ts │ │ │ └── message-api-mocks.ts │ ├── tsconfig.json │ └── tslint.json ├── node │ ├── .env.defaults │ ├── .env.example │ ├── .env.schema │ ├── .gitignore │ ├── README.md │ ├── docs │ │ ├── Makefile │ │ ├── requirements.txt │ │ └── source │ │ │ ├── api.md │ │ │ ├── conf.py │ │ │ ├── diagram.md │ │ │ ├── diagrams │ │ │ ├── node-cfg.mmd │ │ │ └── node-ownership.mmd │ │ │ ├── index.rst │ │ │ └── introduction.md │ ├── jest.config.js │ ├── package.json │ ├── rollup.config.js │ ├── src │ │ ├── api.ts │ │ ├── constants.ts │ │ ├── engine │ │ │ ├── enums.ts │ │ │ ├── index.ts │ │ │ ├── install-virtual-app.ts │ │ │ ├── install.ts │ │ │ ├── middleware.ts │ │ │ ├── propose.ts │ │ │ ├── protocol-runner.ts │ │ │ ├── setup.ts │ │ │ ├── take-action.ts │ │ │ ├── types.ts │ │ │ ├── uninstall-virtual-app.ts │ │ │ ├── uninstall.ts │ │ │ ├── update.ts │ │ │ ├── utils │ │ │ │ ├── get-outcome-increments.ts │ │ │ │ └── signature-validator.ts │ │ │ ├── withdraw.ts │ │ │ └── xkeys.ts │ │ ├── ethereum │ │ │ ├── conditional-transaction-commitment.ts │ │ │ ├── index.ts │ │ │ ├── multisig-commitment.ts │ │ │ ├── set-state-commitment.ts │ │ │ ├── setup-commitment.ts │ │ │ ├── types.ts │ │ │ ├── utils │ │ │ │ ├── app-identity.ts │ │ │ │ ├── encodings.ts │ │ │ │ ├── free-balance-app.ts │ │ │ │ └── index.ts │ │ │ ├── withdraw-erc20-commitment.ts │ │ │ └── withdraw-eth-commitment.ts │ │ ├── index.ts │ │ ├── message-handling │ │ │ ├── handle-node-message.ts │ │ │ └── handle-protocol-message.ts │ │ ├── methods │ │ │ ├── app-instance │ │ │ │ ├── get-all │ │ │ │ │ └── controller.ts │ │ │ │ ├── get-app-instance │ │ │ │ │ └── controller.ts │ │ │ │ ├── get-free-balance │ │ │ │ │ └── controller.ts │ │ │ │ ├── get-state │ │ │ │ │ └── controller.ts │ │ │ │ ├── get-token-indexed-free-balances │ │ │ │ │ └── controller.ts │ │ │ │ ├── install-virtual │ │ │ │ │ ├── controller.ts │ │ │ │ │ └── operation.ts │ │ │ │ ├── install │ │ │ │ │ ├── controller.ts │ │ │ │ │ └── operation.ts │ │ │ │ ├── propose-install │ │ │ │ │ └── controller.ts │ │ │ │ ├── reject-install-virtual │ │ │ │ │ └── controller.ts │ │ │ │ ├── reject-install │ │ │ │ │ └── controller.ts │ │ │ │ ├── take-action │ │ │ │ │ └── controller.ts │ │ │ │ ├── uninstall-virtual │ │ │ │ │ ├── controller.ts │ │ │ │ │ └── operation.ts │ │ │ │ ├── uninstall │ │ │ │ │ ├── controller.ts │ │ │ │ │ └── operation.ts │ │ │ │ └── update-state │ │ │ │ │ └── controller.ts │ │ │ ├── controller.ts │ │ │ ├── errors.ts │ │ │ ├── index.ts │ │ │ ├── proposed-app-instance │ │ │ │ ├── get-all │ │ │ │ │ └── controller.ts │ │ │ │ └── get │ │ │ │ │ └── controller.ts │ │ │ ├── queued-execution.ts │ │ │ └── state-channel │ │ │ │ ├── create │ │ │ │ └── controller.ts │ │ │ │ ├── deploy-state-deposit-holder │ │ │ │ └── controller.ts │ │ │ │ ├── deposit │ │ │ │ ├── controller.ts │ │ │ │ └── operation.ts │ │ │ │ ├── get-all │ │ │ │ └── controller.ts │ │ │ │ ├── get-state-deposit-holder-address │ │ │ │ └── controller.ts │ │ │ │ ├── get │ │ │ │ └── controller.ts │ │ │ │ ├── withdraw-commitment │ │ │ │ └── controller.ts │ │ │ │ └── withdraw │ │ │ │ ├── controller.ts │ │ │ │ └── operation.ts │ │ ├── models │ │ │ ├── app-instance-proposal.ts │ │ │ ├── app-instance.ts │ │ │ ├── free-balance.ts │ │ │ ├── index.ts │ │ │ └── state-channel.ts │ │ ├── network-configuration.ts │ │ ├── node.ts │ │ ├── private-keys-generator.ts │ │ ├── process-queue.ts │ │ ├── request-handler.ts │ │ ├── rpc-router.ts │ │ ├── store.ts │ │ ├── types.ts │ │ └── utils │ │ │ ├── auto-nonce-wallet.ts │ │ │ ├── create2-address.ts │ │ │ ├── deferred.ts │ │ │ └── index.ts │ ├── test │ │ ├── engine │ │ │ ├── integration │ │ │ │ ├── bignumber-jest-matcher.ts │ │ │ │ ├── connect-ganache.ts │ │ │ │ ├── install-then-set-state.spec.ts │ │ │ │ ├── install-virtual.spec.ts │ │ │ │ ├── message-router.ts │ │ │ │ ├── mininode.ts │ │ │ │ ├── protocols │ │ │ │ │ ├── install-uninstall.spec.ts │ │ │ │ │ ├── protocols.spec.ts │ │ │ │ │ └── test-runner.ts │ │ │ │ ├── random-signing-keys.ts │ │ │ │ ├── set-state.spec.ts │ │ │ │ ├── setup-then-set-state.spec.ts │ │ │ │ └── waffle-type.ts │ │ │ ├── mocks.ts │ │ │ └── unit │ │ │ │ ├── ethereum │ │ │ │ ├── conditional-transaction-commitment.spec.ts │ │ │ │ ├── set-state-commitment.spec.ts │ │ │ │ ├── setup-commitment.spec.ts │ │ │ │ └── withdraw-eth-commitment.spec.ts │ │ │ │ ├── models │ │ │ │ ├── app-instance │ │ │ │ │ └── app-instance.spec.ts │ │ │ │ └── state-channel │ │ │ │ │ ├── install.spec.ts │ │ │ │ │ ├── set-state.spec.ts │ │ │ │ │ ├── setup-channel.spec.ts │ │ │ │ │ ├── state-channel.spec.ts │ │ │ │ │ └── uninstall-app.spec.ts │ │ │ │ └── protocol │ │ │ │ └── signature-validator.spec.ts │ │ ├── global-setup.jest.ts │ │ ├── global-teardown.jest.ts │ │ ├── integration │ │ │ ├── cant-take-action.spec.ts │ │ │ ├── cant-uninstall-free-balance.spec.ts │ │ │ ├── channel-creation.spec.ts │ │ │ ├── connext-issue.spec.ts │ │ │ ├── connext-utils.ts │ │ │ ├── deploy-state-deposit-holder.spec.ts │ │ │ ├── get-app-instance.spec.ts │ │ │ ├── get-state-deposit-holder-address.spec.ts │ │ │ ├── get-state.spec.ts │ │ │ ├── install-concurrent-mixed.spec.ts │ │ │ ├── install-concurrent-virtual.spec.ts │ │ │ ├── install-concurrent.spec.ts │ │ │ ├── install-virtual.spec.ts │ │ │ ├── install-with-alternative-signing-scheme.spec.ts │ │ │ ├── install.spec.ts │ │ │ ├── linked-transfer.ts │ │ │ ├── reject-install-virtual.spec.ts │ │ │ ├── reject-install.spec.ts │ │ │ ├── secure-deposit.spec.ts │ │ │ ├── secure-withdraw.spec.ts │ │ │ ├── setup.ts │ │ │ ├── simple-transfer.ts │ │ │ ├── take-action-concurrent-virtual.spec.ts │ │ │ ├── take-action-concurrent.spec.ts │ │ │ ├── take-action-uninstall-virtual-concurrent.spec.ts │ │ │ ├── take-action-virtual.spec.ts │ │ │ ├── take-action.spec.ts │ │ │ ├── tic-tac-toe.ts │ │ │ ├── unidirectional-transfer.ts │ │ │ ├── uninstall-concurrent-mixed.spec.ts │ │ │ ├── uninstall-concurrent-virtual.spec.ts │ │ │ ├── uninstall-concurrent.spec.ts │ │ │ ├── uninstall-install-concurrent.spec.ts │ │ │ ├── uninstall-install-virtual-concurrent.spec.ts │ │ │ ├── uninstall-virtual-install-concurrent.spec.ts │ │ │ ├── uninstall-virtual-install-virtual-concurent.spec.ts │ │ │ ├── uninstall-virtual.spec.ts │ │ │ ├── uninstall.spec.ts │ │ │ └── utils.ts │ │ ├── node-test-environment.jest.js │ │ ├── services │ │ │ ├── lock.ts │ │ │ ├── memory-lock-service.ts │ │ │ ├── memory-messaging-service.ts │ │ │ ├── memory-store-service.ts │ │ │ ├── mock-messaging-service.ts │ │ │ └── mock-store-service.ts │ │ ├── test-constants.jest.ts │ │ └── unit │ │ │ ├── install.spec.ts │ │ │ ├── node.spec.ts │ │ │ ├── process-queue.spec.ts │ │ │ ├── queued-execution.spec.ts │ │ │ └── utils.ts │ ├── truffle-config.js │ ├── tsconfig.json │ └── tslint.json ├── simple-hub-server │ ├── .env-cmdrc │ ├── .env.schema │ ├── .gitignore │ ├── Dockerfile │ ├── Procfile │ ├── README.md │ ├── database │ │ └── schema │ │ │ └── playground_db.sql │ ├── docker-compose.yml │ ├── jest.config.js │ ├── package.json │ ├── registry.json │ ├── registry.local.json │ ├── scripts │ │ ├── heroku-postbuild.sh │ │ └── postinstall.sh │ ├── src │ │ ├── api.ts │ │ ├── db.ts │ │ ├── errors.ts │ │ ├── index.ts │ │ ├── middlewares │ │ │ └── validate-signature.ts │ │ ├── node.ts │ │ ├── resources │ │ │ ├── app │ │ │ │ ├── processor.ts │ │ │ │ └── resource.ts │ │ │ ├── heartbeat │ │ │ │ ├── processor.ts │ │ │ │ └── resource.ts │ │ │ ├── matchmaking-request │ │ │ │ ├── processor.ts │ │ │ │ └── resource.ts │ │ │ ├── multisig-deploy │ │ │ │ ├── processor.ts │ │ │ │ └── resource.ts │ │ │ ├── session-request │ │ │ │ ├── processor.ts │ │ │ │ └── resource.ts │ │ │ └── user │ │ │ │ ├── processor.ts │ │ │ │ └── resource.ts │ │ ├── types.ts │ │ └── utils.ts │ ├── test │ │ ├── api.spec.ts │ │ ├── contract-deployments.jest.ts │ │ ├── global-setup.jest.ts │ │ ├── global-teardown.jest.ts │ │ ├── mock-data.ts │ │ └── node-test-environment.jest.js │ ├── tsconfig.do-not-edit-merged.json │ ├── tsconfig.heroku.json │ ├── tsconfig.json │ └── tslint.json ├── specs │ ├── .gitignore │ ├── Makefile │ ├── README.md │ ├── requirements.txt │ └── source │ │ ├── CONTRIBUTING.md │ │ ├── GLOSSARY.md │ │ ├── adjudication-layer.md │ │ ├── app-definition.md │ │ ├── channel-networks.md │ │ ├── conf.py │ │ ├── diagrams │ │ ├── conditional-transaction-commitment.mmd │ │ ├── install-protocol-exchange.mmd │ │ ├── install-protocol-state.mmd │ │ ├── install-virtual-app-exchange.mmd │ │ ├── setstate-protocol-commitment.mmd │ │ ├── setstate-protocol-exchange.mmd │ │ ├── setstate-protocol-state.mmd │ │ ├── setup-commitment.mmd │ │ ├── setup-protocol-exchange.mmd │ │ ├── setup-protocol-state.mmd │ │ ├── takeaction-protocol-exchange.mmd │ │ ├── uninstall-protocol-commitment.mmd │ │ ├── uninstall-protocol-exchange.mmd │ │ ├── uninstall-protocol-state.mmd │ │ ├── uninstall-virtual-app-exchange.mmd │ │ └── withdraw-exchange.mmd │ │ ├── img │ │ ├── applyAction.svg │ │ ├── resolve.svg │ │ └── statechannel-statuses.svg │ │ ├── index.rst │ │ ├── introduction.md │ │ ├── peer-protocol.md │ │ └── protocols │ │ ├── install-virtual-app.md │ │ ├── install.md │ │ ├── setup.md │ │ ├── take-action.md │ │ ├── uninstall-virtual-app.md │ │ ├── uninstall.md │ │ ├── update.md │ │ └── withdraw.md ├── types │ ├── .gitignore │ ├── package.json │ ├── rollup.config.js │ ├── src │ │ ├── app-instance.ts │ │ ├── data-types.ts │ │ ├── index.js │ │ ├── index.ts │ │ ├── node.ts │ │ └── simple-types.ts │ ├── tsconfig.json │ └── tslint.json ├── typescript-typings │ ├── README.md │ ├── package.json │ ├── tslint.json │ └── types │ │ ├── truffle │ │ └── index.d.ts │ │ └── web3 │ │ └── index.d.ts └── wallet-ui │ ├── .env │ ├── .gitignore │ ├── README.md │ ├── package.json │ ├── public │ ├── assets │ │ └── icon │ │ │ ├── account.svg │ │ │ ├── arrow-active.svg │ │ │ ├── arrow.svg │ │ │ ├── balance.svg │ │ │ ├── channel-app-active.svg │ │ │ ├── channel-app.svg │ │ │ ├── channel-hub-active.svg │ │ │ ├── channel-hub.svg │ │ │ ├── channel-user-active.svg │ │ │ ├── channel-user-grey.svg │ │ │ ├── channel-user.svg │ │ │ ├── close.svg │ │ │ ├── crypto.svg │ │ │ ├── dots-active.svg │ │ │ ├── dots.svg │ │ │ ├── error.svg │ │ │ ├── ethereum.svg │ │ │ ├── favicon.ico │ │ │ ├── high-roller.svg │ │ │ ├── icon.png │ │ │ ├── login.svg │ │ │ ├── logo.jpg │ │ │ ├── logo.svg │ │ │ ├── menu-btn.svg │ │ │ ├── register.svg │ │ │ └── wallet.svg │ ├── favicon.ico │ ├── index.html │ └── manifest.json │ ├── src │ ├── App.scss │ ├── App.test.tsx │ ├── App.tsx │ ├── components │ │ ├── account │ │ │ ├── account-context │ │ │ │ ├── AccountContext.mock.json │ │ │ │ ├── AccountContext.scss │ │ │ │ ├── AccountContext.spec.tsx │ │ │ │ └── AccountContext.tsx │ │ │ └── index.ts │ │ ├── channel │ │ │ ├── channel-node │ │ │ │ ├── ChannelNode.scss │ │ │ │ ├── ChannelNode.test.tsx │ │ │ │ └── ChannelNode.tsx │ │ │ ├── channel-tree │ │ │ │ ├── ChannelTree.scss │ │ │ │ └── ChannelTree.tsx │ │ │ └── index.ts │ │ ├── form │ │ │ ├── form-button │ │ │ │ ├── FormButton.scss │ │ │ │ ├── FormButton.test.tsx │ │ │ │ └── FormButton.tsx │ │ │ ├── form-input │ │ │ │ ├── FormInput.scss │ │ │ │ └── FormInput.tsx │ │ │ └── index.ts │ │ ├── layout │ │ │ ├── index.ts │ │ │ └── layout-header │ │ │ │ ├── LayoutHeader.scss │ │ │ │ ├── LayoutHeader.test.tsx │ │ │ │ └── LayoutHeader.tsx │ │ └── widget │ │ │ ├── index.ts │ │ │ ├── widget-card │ │ │ ├── WidgetCard.scss │ │ │ └── WidgetCard.tsx │ │ │ ├── widget-error-message │ │ │ ├── WidgetErrorMessage.scss │ │ │ ├── WidgetErrorMessage.test.tsx │ │ │ └── WidgetErrorMessage.tsx │ │ │ ├── widget-header │ │ │ ├── WidgetHeader.scss │ │ │ └── WidgetHeader.tsx │ │ │ ├── widget-logo │ │ │ ├── WidgetLogo.scss │ │ │ ├── WidgetLogo.test.tsx │ │ │ └── WidgetLogo.tsx │ │ │ ├── widget-screen │ │ │ ├── WidgetScreen.scss │ │ │ └── WidgetScreen.tsx │ │ │ ├── widget-spinner │ │ │ ├── WidgetSpinner.scss │ │ │ └── WidgetSpinner.tsx │ │ │ └── widget-tooltip │ │ │ ├── WidgetTooltip.scss │ │ │ └── WidgetTooltip.tsx │ ├── index.tsx │ ├── logo.svg │ ├── pages │ │ ├── account-balance │ │ │ └── AccountBalance.tsx │ │ ├── account-deposit │ │ │ ├── AccountDeposit.context.json │ │ │ ├── AccountDeposit.scss │ │ │ ├── AccountDeposit.spec.tsx │ │ │ └── AccountDeposit.tsx │ │ ├── account-registration │ │ │ ├── AccountRegistration.context.json │ │ │ ├── AccountRegistration.scss │ │ │ ├── AccountRegistration.spec.tsx │ │ │ └── AccountRegistration.tsx │ │ ├── account-withdraw │ │ │ ├── AccountWithdraw.context.json │ │ │ ├── AccountWithdraw.scss │ │ │ ├── AccountWithdraw.spec.tsx │ │ │ └── AccountWithdraw.tsx │ │ ├── channels │ │ │ ├── Channels.context.json │ │ │ ├── Channels.scss │ │ │ ├── Channels.spec.tsx │ │ │ └── Channels.tsx │ │ ├── index.ts │ │ └── welcome │ │ │ ├── Welcome.scss │ │ │ ├── Welcome.spec.tsx │ │ │ └── Welcome.tsx │ ├── providers │ │ └── EthereumService.tsx │ ├── react-app-env.d.ts │ ├── setupTests.ts │ ├── store │ │ ├── channels │ │ │ ├── channels.mock.ts │ │ │ ├── channels.spec.ts │ │ │ └── channels.ts │ │ ├── store.mock.ts │ │ ├── store.ts │ │ ├── test-utils │ │ │ ├── call-action.ts │ │ │ ├── ethereum.mock.ts │ │ │ ├── hub-api-client.mock.ts │ │ │ ├── json-rpc-signer.mock.ts │ │ │ ├── nodeTokenClient.ts │ │ │ └── web3provider.mock.ts │ │ ├── types.ts │ │ ├── user │ │ │ ├── user.mock.ts │ │ │ ├── user.spec.ts │ │ │ └── user.ts │ │ └── wallet │ │ │ ├── wallet.mock.ts │ │ │ ├── wallet.spec.ts │ │ │ └── wallet.ts │ ├── styles │ │ ├── _box-sizing.scss │ │ ├── _button.scss │ │ ├── _layout.scss │ │ ├── _reset.scss │ │ ├── _responsive.scss │ │ ├── _typography.scss │ │ └── _variables.scss │ ├── types.ts │ └── utils │ │ ├── counterfactual.spec.ts │ │ ├── counterfactual.ts │ │ ├── delay.ts │ │ ├── hub-api-client.ts │ │ ├── log.ts │ │ ├── nodeTokenClient.ts │ │ └── testSelector.ts │ ├── tsconfig.json │ └── tslint.json ├── patches ├── .gitkeep ├── @resolver-engine+imports-fs+0.3.3.patch ├── ethereum-waffle+2.1.0.patch └── scrypt+6.0.3.patch ├── renovate.json ├── tsconfig.json ├── tslint.json ├── waffle.js └── yarn.lock /.circleci/README.md: -------------------------------------------------------------------------------- 1 | # CircleCI: Continuous Integration Notes 2 | 3 | Currently the [`Dockerfile`](./images/build/Dockerfile) in this folder is being autobuilt by [Docker Hub](https://cloud.docker.com/u/counterfactual/repository/docker/counterfactual/circleci-environment/builds) on the `master` branch only and then used inside the [`config.yml`](./config.yml) tagged as `counterfactual/circleci-environment:latest`. 4 | 5 | Here are the settings used when the autobuild was configured: 6 | 7 | | Build Rule | Value | 8 | |--------------------- |----------------------------------- | 9 | | Source | master | 10 | | Docker Tag | latest | 11 | | Dockerfile Location | .circleci/images/build/Dockerfile | 12 | | Build Context | / | 13 | | Autobuild | True | 14 | | Build Caching | True | 15 | -------------------------------------------------------------------------------- /.circleci/images/build/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM resinci/npm-x86_64-ubuntu-node10 2 | 3 | # add ethereum ppa 4 | RUN add-apt-repository ppa:ethereum/ethereum 5 | 6 | RUN apt-get update 7 | 8 | # there doesn't appear to be any way to specify solc version num 9 | # https://launchpad.net/~ethereum/+archive/ubuntu/ethereum/+packages?field.name_filter=solc&field.status_filter=published&field.series_filter= 10 | RUN apt-get install -y solc 11 | 12 | # install node 10.x and yarn 1.10.x 13 | RUN apt-get install -y curl && \ 14 | curl -sL https://deb.nodesource.com/node_10.x/pool/main/n/nodejs/nodejs_10.15.3-1nodesource1_amd64.deb > nodejs-10.15.3.deb && \ 15 | dpkg -i nodejs-10.15.3.deb && \ 16 | rm /usr/local/bin/node && \ 17 | npm install -g yarn@1.19.0 && \ 18 | rm /usr/local/bin/yarn 19 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | tab_width = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | max_line_length = 80 12 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.sol linguist-language=Solidity 2 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | /* @snario 2 | /packages/apps @snario 3 | /packages/cf.js @ebryn 4 | /packages/cf-adjudicator-contracts @IIIIllllIIIIllllIIIIllllIIIIllllIIIIll 5 | /packages/cf-funding-protocol-contracts @IIIIllllIIIIllllIIIIllllIIIIllllIIIIll 6 | /packages/dapp-tic-tac-toe @patience-tema-baron 7 | /packages/dapp-high-roller @Alonski 8 | /packages/high-roller-bot @cf19drofxots 9 | /packages/machine @snario 10 | /packages/node @cf19drofxots 11 | /packages/node-provider @ebryn 12 | /packages/playground @joelalejandro 13 | /packages/simple-hub-server @joelalejandro 14 | /packages/specs @IIIIllllIIIIllllIIIIllllIIIIllllIIIIll 15 | /packages/tic-tac-toe-bot @cf19drofxots 16 | /packages/types @snario 17 | /packages/typescript-typings @snario 18 | /packages/wallet-ui @joelalejandro @spersico 19 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | > Yay! You're creating a new issue. Keep in mind these guidelines: 2 | > 3 | > - Set the title following this convention: 4 | > 5 | > [feature-group][summary] 6 | > 7 | > where: 8 | > 9 | > - `feature-group` represents either a monorepo package, or other features such as "documentation" or "lint". 10 | > - `summary` is a one-sentence version of the issue description. 11 | > 12 | > - Tag the PR appropriately: 13 | > - If you're interested in discussing something in particular, use the "question" label. 14 | > - If you're reporting something that doesn't look right in the repo, use the "bug" or "invalid" labels _(bug = something's broken, invalid = something's weird)_. 15 | > - If it's related to a package, use the corresponding label (i.e. _"Package: machine"_). 16 | > - If what you're discussing has dependencies with other contributors or external circumstances, tag it with the "waiting" label. 17 | > 18 | > Once you're good to go, delete these guidelines :) 19 | 20 | [Discuss here!] 21 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | > Yay! You're creating a new PR. Keep in mind these guidelines: 2 | > 3 | > - Set the title following this convention: 4 | > 5 | > [feature-group][change-summary] 6 | > 7 | > where: 8 | > 9 | > - `feature-group` represents either a monorepo package, or other features such as "documentation" or "lint". 10 | > - `change-summary` is a one-sentence version of the PR description. 11 | > 12 | > - Tag the PR appropriately: 13 | > - If you're early-opening a PR in do further work on it, tag it with the "in progress" label. 14 | > - If it belongs to a package, use the corresponding label (i.e. _"Package: machine"_). 15 | > - If it updates documentation, tag it with the "documentation" label. 16 | > - If your work has dependencies with other contributors, tag it with the "waiting" label. 17 | > 18 | > Once you're good to go, delete these guidelines :) 19 | 20 | ### Description 21 | 22 | [Explain what are you attempting to accomplish with these changes.] 23 | 24 | ### Related issues 25 | 26 | [Link here any issues relevant to this PR, using the GitHub `fixes/resolves/closes` keywords to close related issues automatically.] 27 | 28 | - [ ] Deploy preview is functional 29 | -------------------------------------------------------------------------------- /.github/SUPPORT.md: -------------------------------------------------------------------------------- 1 | # Getting Support 2 | 3 | If you're trying to learn more about the project, make a contribution, or just want to chat with the core team then you can use the following resources: 4 | 5 | - Visit our website at [counterfactual.com](https://counterfactual.com) 6 | - Chat with us on [Discord](https://counterfactual.com/chat) 7 | - Send an email to [hello@counterfactual.com](mailto://hello@counterfactual.com) 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # dependencies 2 | node_modules 3 | packages/**/yarn.lock 4 | 5 | # production 6 | build/ 7 | dist/ 8 | functions/ 9 | 10 | # cache 11 | .config 12 | .node-gyp 13 | .npm 14 | .rpt2_cache 15 | 16 | # coverage 17 | coverage 18 | 19 | # misc 20 | package-lock.json 21 | .DS_Store 22 | .vscode 23 | .idea/ 24 | *.iml 25 | *.sw[op] 26 | 27 | npm-debug.log* 28 | yarn-debug.log* 29 | yarn-error.log* 30 | lerna-debug.log* 31 | 32 | jest-cache/ 33 | e2e.log 34 | 35 | # local env vars 36 | .env.local 37 | packages/**/.env.local 38 | 39 | # licenses 40 | packages/**/LICENSE 41 | 42 | # circleci artifacts 43 | .dependencies_checksum 44 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | v10.15.3 2 | -------------------------------------------------------------------------------- /.soliumrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "solium:all", 3 | "plugins": ["security"], 4 | "rules": { 5 | "arg-overflow": ["error", 8], 6 | "array-declarations": 1, 7 | "camelcase": 1, 8 | "deprecated-suicide": 1, 9 | "imports-on-top": 1, 10 | "indentation": ["error", 2], 11 | "lbrace": 0, 12 | "max-len": ["error", 80], 13 | "mixedcase": 0, 14 | "no-empty-blocks": 1, 15 | "no-experimental": 0, 16 | "no-unused-vars": 1, 17 | "operator-whitespace": 1, 18 | "pragma-on-top": 1, 19 | "quotes": 1, 20 | "security/enforce-explicit-visibility": ["error"], 21 | "security/no-block-members": ["warning"], 22 | "security/no-inline-assembly": 0, 23 | "security/no-low-level-calls": 0, 24 | "uppercase": 1, 25 | "variable-declarations": 1, 26 | "whitespace": 1 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /.yarnrc: -------------------------------------------------------------------------------- 1 | workspaces-experimental true 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright © 2018 Counterfactual contributors 2 | 3 | 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: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | 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. 8 | -------------------------------------------------------------------------------- /bin/README.md: -------------------------------------------------------------------------------- 1 | # `bin`: Heroku Buildpack Scripts 2 | 3 | These files are used by Heroku to explain to the deployer how to handle deploying a package on a dyno. -------------------------------------------------------------------------------- /bin/compile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | indent() { 3 | sed -u 's/^/ /' 4 | } 5 | 6 | BUILD_DIR="$1" 7 | CACHE_DIR="$2" 8 | ENV_DIR="$3" 9 | STAGE="$(mktemp -d)" 10 | 11 | if [ ! -f "${ENV_DIR}/APP_BASE" ]; then 12 | echo "APP_BASE was not set. Aborting" | indent 13 | exit 1 14 | fi 15 | APP_BASE="$(cat "${ENV_DIR}/APP_BASE")" 16 | 17 | ( 18 | mv "${BUILD_DIR}/${APP_BASE}" "${STAGE}" && 19 | rm -rf "${BUILD_DIR}" && 20 | mv "${STAGE}/$(basename "$APP_BASE")" "${BUILD_DIR}" 21 | ) 22 | 23 | if [ $? -ne 0 ]; then 24 | echo "FAILED to copy directory into place" | indent 25 | exit 1 26 | fi 27 | 28 | echo "Copied ${APP_BASE} to root of app successfully" | indent 29 | -------------------------------------------------------------------------------- /bin/detect: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | echo "Monorepo" 4 | exit 0 -------------------------------------------------------------------------------- /bin/release: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | echo "--- {}" 4 | exit 0 -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "bail": true, 3 | "coverageDirectory": "jest-coverage", 4 | "coveragePathIgnorePatterns": [ 5 | "test" 6 | ], 7 | "coverageThreshold": { 8 | "global": { 9 | "branches": 90, 10 | "functions": 90, 11 | "lines": 90, 12 | "statements": 90 13 | } 14 | }, 15 | "moduleFileExtensions": [ 16 | "ts", 17 | "js", 18 | "json" 19 | ], 20 | "rootDir": ".", 21 | "roots": [ 22 | "test" 23 | ], 24 | "testPathIgnorePatterns": [ 25 | "node_modules", 26 | "dist" 27 | ], 28 | "testRegex": "\\.spec.(jsx?|tsx?)$", 29 | "testURL": "http://localhost/", 30 | "transform": { 31 | "^.+\\.tsx?$": "ts-jest" 32 | }, 33 | "verbose": false, 34 | "extraGlobals": ["Math"] 35 | } 36 | -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "lerna": "3.4.3", 3 | "npmClient": "yarn", 4 | "useWorkspaces": true, 5 | "packages": [ 6 | "packages/*" 7 | ], 8 | "version": "independent", 9 | "publishConfig": { 10 | "access": "public", 11 | "registry": "https://registry.npmjs.org" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /packages/apps/.env.example: -------------------------------------------------------------------------------- 1 | ETH_ACCOUNT_MNENOMIC="some mnemonic" 2 | INFURA_API_KEY="" 3 | DEFAULT_GAS=7000000 4 | DEFAULT_GAS_PRICE=30000000000 5 | -------------------------------------------------------------------------------- /packages/apps/.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | .env 3 | -------------------------------------------------------------------------------- /packages/apps/.solcover.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | testCommand: 'truffle test --network coverage lib/**/*.spec.js', 3 | skipFiles: [ 4 | "external/Proxy.sol", 5 | "external/ProxyFactory.sol", 6 | "lib/StaticCall.sol", 7 | "Registry.sol", 8 | "test/loadContracts.sol" 9 | ] 10 | }; 11 | -------------------------------------------------------------------------------- /packages/apps/.soliumignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | coverageEnv 3 | -------------------------------------------------------------------------------- /packages/apps/.soliumrc.json: -------------------------------------------------------------------------------- 1 | ../../.soliumrc.json -------------------------------------------------------------------------------- /packages/apps/contracts/Migrations.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.5.12; 2 | 3 | 4 | contract Migrations { 5 | address public owner; 6 | uint public last_completed_migration; 7 | 8 | modifier restricted() { 9 | if (msg.sender == owner) 10 | _; 11 | } 12 | 13 | constructor() public { 14 | owner = msg.sender; 15 | } 16 | 17 | function setCompleted(uint completed) public restricted { 18 | last_completed_migration = completed; 19 | } 20 | 21 | function upgrade(address new_address) public restricted { 22 | Migrations upgraded = Migrations(new_address); 23 | upgraded.setCompleted(last_completed_migration); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /packages/apps/contracts/SimpleTransferApp.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.5.12; 2 | pragma experimental "ABIEncoderV2"; 3 | 4 | /* solium-disable-next-line */ 5 | import "@counterfactual/cf-adjudicator-contracts/contracts/interfaces/CounterfactualApp.sol"; 6 | /* solium-disable-next-line */ 7 | import "@counterfactual/cf-funding-protocol-contracts/contracts/libs/LibOutcome.sol"; 8 | import "openzeppelin-solidity/contracts/math/SafeMath.sol"; 9 | 10 | 11 | /// @title SimpleTwoPartySwapApp 12 | /// @notice This contract lets Alice transfer assets to Bob 13 | contract SimpleTransferApp is CounterfactualApp { 14 | 15 | using SafeMath for uint256; 16 | 17 | struct AppState { 18 | LibOutcome.CoinTransfer[2] coinTransfers; 19 | } 20 | 21 | function computeOutcome(bytes memory encodedState) 22 | public 23 | pure 24 | returns (bytes memory) 25 | { 26 | AppState memory state = abi.decode(encodedState, (AppState)); 27 | 28 | uint256 transferAmount = state.coinTransfers[0].amount; 29 | 30 | state.coinTransfers[0].amount = 0; 31 | state.coinTransfers[1].amount = transferAmount; 32 | 33 | return abi.encode(state.coinTransfers); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /packages/apps/contracts/SimpleTwoPartySwapApp.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.5.12; 2 | pragma experimental "ABIEncoderV2"; 3 | 4 | /* solium-disable-next-line */ 5 | import "@counterfactual/cf-adjudicator-contracts/contracts/interfaces/CounterfactualApp.sol"; 6 | /* solium-disable-next-line */ 7 | import "@counterfactual/cf-funding-protocol-contracts/contracts/libs/LibOutcome.sol"; 8 | import "openzeppelin-solidity/contracts/math/SafeMath.sol"; 9 | 10 | 11 | /// @title SimpleTwoPartySwapApp 12 | /// @notice This contract lets two parties swap one ERC20 or ETH asset for another 13 | contract SimpleTwoPartySwapApp is CounterfactualApp { 14 | 15 | using SafeMath for uint256; 16 | 17 | struct AppState { 18 | LibOutcome.CoinTransfer[][] coinTransfers; 19 | } 20 | 21 | function computeOutcome(bytes memory encodedState) 22 | public 23 | pure 24 | returns (bytes memory) 25 | { 26 | AppState memory state = abi.decode(encodedState, (AppState)); 27 | 28 | uint256 amountsA = state.coinTransfers[0][0].amount; 29 | uint256 amountsB = state.coinTransfers[1][0].amount; 30 | 31 | state.coinTransfers[0][0].amount = amountsB; 32 | state.coinTransfers[1][0].amount = amountsA; 33 | 34 | return abi.encode(state.coinTransfers); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /packages/apps/expected-build-artifacts/LibOutcome.json: -------------------------------------------------------------------------------- 1 | { 2 | "abi": [], 3 | "evm": { 4 | "bytecode": { 5 | "linkReferences": {}, 6 | "object": "60636023600b82828239805160001a607314601657fe5b30600052607381538281f3fe73000000000000000000000000000000000000000030146080604052600080fdfea365627a7a723158202b8ab300213565be6647b464acc0e040325a97eda0094dada9aa3b042e99fab16c6578706572696d656e74616cf564736f6c634300050c0040", 7 | "opcodes": "PUSH1 0x63 PUSH1 0x23 PUSH1 0xB DUP3 DUP3 DUP3 CODECOPY DUP1 MLOAD PUSH1 0x0 BYTE PUSH1 0x73 EQ PUSH1 0x16 JUMPI INVALID JUMPDEST ADDRESS PUSH1 0x0 MSTORE PUSH1 0x73 DUP2 MSTORE8 DUP3 DUP2 RETURN INVALID PUSH20 0x0 ADDRESS EQ PUSH1 0x80 PUSH1 0x40 MSTORE PUSH1 0x0 DUP1 REVERT INVALID LOG3 PUSH6 0x627A7A723158 KECCAK256 0x2b DUP11 0xb3 STOP 0x21 CALLDATALOAD PUSH6 0xBE6647B464AC 0xc0 0xe0 BLOCKHASH ORIGIN GAS SWAP8 0xed LOG0 MULMOD 0x4d 0xad 0xa9 0xaa EXTCODESIZE DIV 0x2e SWAP10 STATICCALL 0xb1 PUSH13 0x6578706572696D656E74616CF5 PUSH5 0x736F6C6343 STOP SDIV 0xc STOP BLOCKHASH ", 8 | "sourceMap": "62:208:1:-;;132:2:-1;166:7;155:9;146:7;137:37;255:7;249:14;246:1;241:23;235:4;232:33;222:2;;269:9;222:2;293:9;290:1;283:20;323:4;314:7;306:22;347:7;338;331:24" 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /packages/apps/expected-build-artifacts/SafeMath.json: -------------------------------------------------------------------------------- 1 | { 2 | "abi": [], 3 | "evm": { 4 | "bytecode": { 5 | "linkReferences": {}, 6 | "object": "60556023600b82828239805160001a607314601657fe5b30600052607381538281f3fe73000000000000000000000000000000000000000030146080604052600080fdfea265627a7a72315820afd17dd44ee66f15691980af06b957c9083494fd0b4dce655636b221bd8feb7164736f6c634300050c0032", 7 | "opcodes": "PUSH1 0x55 PUSH1 0x23 PUSH1 0xB DUP3 DUP3 DUP3 CODECOPY DUP1 MLOAD PUSH1 0x0 BYTE PUSH1 0x73 EQ PUSH1 0x16 JUMPI INVALID JUMPDEST ADDRESS PUSH1 0x0 MSTORE PUSH1 0x73 DUP2 MSTORE8 DUP3 DUP2 RETURN INVALID PUSH20 0x0 ADDRESS EQ PUSH1 0x80 PUSH1 0x40 MSTORE PUSH1 0x0 DUP1 REVERT INVALID LOG2 PUSH6 0x627A7A723158 KECCAK256 0xaf 0xd1 PUSH30 0xD44EE66F15691980AF06B957C9083494FD0B4DCE655636B221BD8FEB7164 PUSH20 0x6F6C634300050C00320000000000000000000000 ", 8 | "sourceMap": "589:2938:10:-;;132:2:-1;166:7;155:9;146:7;137:37;255:7;249:14;246:1;241:23;235:4;232:33;222:2;;269:9;222:2;293:9;290:1;283:20;323:4;314:7;306:22;347:7;338;331:24" 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /packages/apps/migrations/1_initial_migration.js: -------------------------------------------------------------------------------- 1 | const Migrations = artifacts.require("./Migrations.sol"); 2 | 3 | module.exports = function(deployer, network) { 4 | deployer.deploy(Migrations); 5 | }; 6 | -------------------------------------------------------------------------------- /packages/apps/migrations/2_deploy_contracts.js: -------------------------------------------------------------------------------- 1 | const tdr = require("truffle-deploy-registry"); 2 | 3 | const ARTIFACTS = [ 4 | artifacts.require("HighRollerApp"), 5 | artifacts.require("NimApp"), 6 | artifacts.require("TicTacToeApp"), 7 | artifacts.require("UnidirectionalTransferApp"), 8 | artifacts.require("SimpleTwoPartySwapApp") 9 | ]; 10 | 11 | module.exports = (deployer, network) => { 12 | deployer.then(async () => { 13 | for (const artifact of ARTIFACTS) { 14 | const instance = await deployer.deploy(artifact); 15 | if (!tdr.isDryRunNetworkName(network)) { 16 | await tdr.appendInstance(instance); 17 | } 18 | } 19 | }); 20 | }; 21 | -------------------------------------------------------------------------------- /packages/apps/networks/4.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "contractName": "UnidirectionalTransferApp", 4 | "address": "0xe99AC20B16d5C9117441Cd5D868434a17858Ff4a", 5 | "transactionHash": "0xd42f33cf83b7ad7d074a0071cf3177ff0e8961b8278d0a411c1318d68477acfd" 6 | } 7 | ] -------------------------------------------------------------------------------- /packages/apps/networks/42.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "contractName": "HighRollerApp", 4 | "address": "0x9F13f1f6810554CAe575E34e74F6dF6dA190aE3B", 5 | "transactionHash": "0x27fdbb8a602891b74142ca31afa02334c5ed74ed888cce01970da4c641b72df7" 6 | }, 7 | { 8 | "contractName": "NimApp", 9 | "address": "0xE8D589d61BB6C790cdE42d03D9493F10443eC831", 10 | "transactionHash": "0xb422be30c60ee09d45c63fc09d64f3a81aedff3a8c2e2a74bb6fd013066443fc" 11 | }, 12 | { 13 | "contractName": "TicTacToeApp", 14 | "address": "0x3d2623d5fcAf027BD3c9286E4Fb0CdEF52807532", 15 | "transactionHash": "0x025500ae8cf12ccdd75c052bd9f0b661b264c39fafb2bf98102fb2c463fde7b6" 16 | }, 17 | { 18 | "contractName": "UnidirectionalTransferApp", 19 | "address": "0x137c6c23109B1Fc0e4D21e4379B4C565a974d46e", 20 | "transactionHash": "0xe9533f8b3292bdc45a6aa11493c2c704d9acabfd5acd4c8309695907ad1c49b0" 21 | }, 22 | { 23 | "contractName": "SimpleTwoPartySwapApp", 24 | "address": "0xB928A2bc6735B5B3CEcEA813B880C49AC82c771a", 25 | "transactionHash": "0x2522c9ca8521cebc08f140a2e59c85741ed8b0608f949f8cba5f91cab68e5c6a" 26 | } 27 | ] -------------------------------------------------------------------------------- /packages/apps/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "module": "commonjs", 5 | "noImplicitAny": true 6 | }, 7 | "include": ["test"] 8 | } 9 | -------------------------------------------------------------------------------- /packages/apps/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../../tslint.json"], 3 | "linterOptions": { 4 | "exclude": ["**/*.json"] 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /packages/apps/waffle.js: -------------------------------------------------------------------------------- 1 | const selectSolc = require("../../waffle.js"); 2 | 3 | module.exports = selectSolc(); 4 | -------------------------------------------------------------------------------- /packages/cf-adjudicator-contracts/.env.example: -------------------------------------------------------------------------------- 1 | ETH_ACCOUNT_MNENOMIC="some mnemonic" 2 | INFURA_API_KEY="" 3 | DEFAULT_GAS=7000000 4 | NATIVE_SOLC=true 5 | -------------------------------------------------------------------------------- /packages/cf-adjudicator-contracts/.gitignore: -------------------------------------------------------------------------------- 1 | networks/*.json 2 | 3 | # Olympic, Ethereum public pre-release testnet 4 | !networks/0.json 5 | 6 | # Frontier, Homestead, Metropolis, the Ethereum public main network 7 | !networks/1.json 8 | 9 | # Morden, the public Ethereum testnet, now Ethereum Classic testnet 10 | !networks/2.json 11 | 12 | # Ropsten, the public cross-client Ethereum testnet 13 | !networks/3.json 14 | 15 | # Rinkeby, the public Geth PoA testnet 16 | !networks/4.json 17 | 18 | # Ubiq, the public Gubiq main network with flux difficulty chain ID 8 19 | !networks/8.json 20 | 21 | # Kovan, the public Parity PoA testnet 22 | !networks/42.json 23 | 24 | # Sokol, the public POA Network testnet 25 | !networks/77.json 26 | 27 | # Core, the public POA Network main network 28 | !networks/99.json 29 | 30 | # xDai, the public MakerDAO/POA Network main network 31 | !networks/100.json 32 | 33 | # Tobalaba, the public Energy Web Foundation testnet 34 | !networks/401697.json 35 | 36 | # Musicoin, the music blockchain 37 | !networks/7762959.json 38 | 39 | # Aquachain, ASIC resistant chain 40 | !networks/61717561.json 41 | 42 | .env 43 | 44 | # NOTE: These shouldn't even be getting generated anymore. 45 | # Keeping here for devs with stale files saved locally. 46 | test/**/*.js 47 | test/**/*js.map 48 | test/**/*.d.ts 49 | -------------------------------------------------------------------------------- /packages/cf-adjudicator-contracts/.solcover.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | testCommand: 'truffle test --network coverage lib/*.spec.js', 3 | skipFiles: [ 4 | "proxies/Proxy.sol", 5 | "proxies/ProxyFactory.sol" 6 | ] 7 | }; 8 | -------------------------------------------------------------------------------- /packages/cf-adjudicator-contracts/.soliumignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | coverageEnv 3 | contracts/interpreters/MultiAssetMultiPartyCoinTransferInterpreter.sol 4 | contracts/interpreters/SwapInterpreter.sol 5 | -------------------------------------------------------------------------------- /packages/cf-adjudicator-contracts/.soliumrc.json: -------------------------------------------------------------------------------- 1 | ../../.soliumrc.json -------------------------------------------------------------------------------- /packages/cf-adjudicator-contracts/contracts/ChallengeRegistry.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.5.12; 2 | pragma experimental "ABIEncoderV2"; 3 | 4 | import "./mixins/MixinChallengeRegistryCore.sol"; 5 | import "./mixins/MixinCancelChallenge.sol"; 6 | import "./mixins/MixinSetOutcome.sol"; 7 | import "./mixins/MixinSetState.sol"; 8 | import "./mixins/MixinSetStateWithAction.sol"; 9 | import "./mixins/MixinRespondToChallenge.sol"; 10 | 11 | 12 | /// @dev Base contract implementing all logic needed for full-featured App registry 13 | contract ChallengeRegistry is 14 | MixinChallengeRegistryCore, 15 | MixinSetState, 16 | MixinSetStateWithAction, 17 | MixinCancelChallenge, 18 | MixinRespondToChallenge, 19 | MixinSetOutcome 20 | { 21 | // solium-disable-next-line no-empty-blocks 22 | constructor () public {} 23 | } 24 | -------------------------------------------------------------------------------- /packages/cf-adjudicator-contracts/contracts/Migrations.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.5.12; 2 | 3 | 4 | contract Migrations { 5 | address public owner; 6 | uint public last_completed_migration; 7 | 8 | modifier restricted() { 9 | if (msg.sender == owner) 10 | _; 11 | } 12 | 13 | constructor() public { 14 | owner = msg.sender; 15 | } 16 | 17 | function setCompleted(uint completed) public restricted { 18 | last_completed_migration = completed; 19 | } 20 | 21 | function upgrade(address new_address) public restricted { 22 | Migrations upgraded = Migrations(new_address); 23 | upgraded.setCompleted(last_completed_migration); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /packages/cf-adjudicator-contracts/contracts/interfaces/CounterfactualApp.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.5.12; 2 | pragma experimental "ABIEncoderV2"; 3 | 4 | 5 | contract CounterfactualApp { 6 | 7 | function isStateTerminal(bytes memory) 8 | public 9 | pure 10 | returns (bool) 11 | { 12 | revert("The isStateTerminal method has no implementation for this App"); 13 | } 14 | 15 | function getTurnTaker(bytes memory, address[] memory) 16 | public 17 | pure 18 | returns (address) 19 | { 20 | revert("The getTurnTaker method has no implementation for this App"); 21 | } 22 | 23 | function applyAction(bytes memory, bytes memory) 24 | public 25 | pure 26 | returns (bytes memory) 27 | { 28 | revert("The applyAction method has no implementation for this App"); 29 | } 30 | 31 | function computeOutcome(bytes memory) 32 | public 33 | pure 34 | returns (bytes memory) 35 | { 36 | revert("The computeOutcome method has no implementation for this App"); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /packages/cf-adjudicator-contracts/expected-build-artifacts/ECDSA.json: -------------------------------------------------------------------------------- 1 | { 2 | "abi": [], 3 | "evm": { 4 | "bytecode": { 5 | "linkReferences": {}, 6 | "object": "60556023600b82828239805160001a607314601657fe5b30600052607381538281f3fe73000000000000000000000000000000000000000030146080604052600080fdfea265627a7a723158206b32ed53f797bd8cf556e4627a0b7428ba4bb302f0858e5a16cdbc34b58616e964736f6c634300050c0032", 7 | "opcodes": "PUSH1 0x55 PUSH1 0x23 PUSH1 0xB DUP3 DUP3 DUP3 CODECOPY DUP1 MLOAD PUSH1 0x0 BYTE PUSH1 0x73 EQ PUSH1 0x16 JUMPI INVALID JUMPDEST ADDRESS PUSH1 0x0 MSTORE PUSH1 0x73 DUP2 MSTORE8 DUP3 DUP2 RETURN INVALID PUSH20 0x0 ADDRESS EQ PUSH1 0x80 PUSH1 0x40 MSTORE PUSH1 0x0 DUP1 REVERT INVALID LOG2 PUSH6 0x627A7A723158 KECCAK256 PUSH12 0x32ED53F797BD8CF556E4627A SIGNEXTEND PUSH21 0x28BA4BB302F0858E5A16CDBC34B58616E964736F6C PUSH4 0x4300050C STOP ORIGIN ", 8 | "sourceMap": "231:3422:13:-;;132:2:-1;166:7;155:9;146:7;137:37;255:7;249:14;246:1;241:23;235:4;232:33;222:2;;269:9;222:2;293:9;290:1;283:20;323:4;314:7;306:22;347:7;338;331:24" 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /packages/cf-adjudicator-contracts/expected-build-artifacts/LibAppCaller.json: -------------------------------------------------------------------------------- 1 | { 2 | "abi": [], 3 | "evm": { 4 | "bytecode": { 5 | "linkReferences": {}, 6 | "object": "6080604052348015600f57600080fd5b50604c80601d6000396000f3fe6080604052600080fdfea365627a7a72315820a6e68d610d76b420a483f9a162aa05c85b2ba6adbf5b66a04a775ec3799207fa6c6578706572696d656e74616cf564736f6c634300050c0040", 7 | "opcodes": "PUSH1 0x80 PUSH1 0x40 MSTORE CALLVALUE DUP1 ISZERO PUSH1 0xF JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x4C DUP1 PUSH1 0x1D PUSH1 0x0 CODECOPY PUSH1 0x0 RETURN INVALID PUSH1 0x80 PUSH1 0x40 MSTORE PUSH1 0x0 DUP1 REVERT INVALID LOG3 PUSH6 0x627A7A723158 KECCAK256 0xa6 0xe6 DUP14 PUSH2 0xD76 0xb4 KECCAK256 LOG4 DUP4 0xf9 LOG1 PUSH3 0xAA05C8 JUMPDEST 0x2b 0xa6 0xad 0xbf JUMPDEST PUSH7 0xA04A775EC37992 SMOD STATICCALL PUSH13 0x6578706572696D656E74616CF5 PUSH5 0x736F6C6343 STOP SDIV 0xc STOP BLOCKHASH ", 8 | "sourceMap": "248:2123:3:-;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;248:2123:3;;;;;;;" 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /packages/cf-adjudicator-contracts/migrations/1_initial_migration.js: -------------------------------------------------------------------------------- 1 | const tdr = require("truffle-deploy-registry"); 2 | 3 | const Migrations = artifacts.require("./Migrations.sol"); 4 | 5 | module.exports = function(deployer, network) { 6 | 7 | const addToNetworkFile = (obj) => 8 | tdr.isDryRunNetworkName(network) || tdr.appendInstance(obj); 9 | 10 | deployer.deploy(Migrations).then(addToNetworkFile); 11 | 12 | }; 13 | -------------------------------------------------------------------------------- /packages/cf-adjudicator-contracts/migrations/2_deploy_contracts.js: -------------------------------------------------------------------------------- 1 | const tdr = require("truffle-deploy-registry"); 2 | 3 | const ARTIFACTS = [ 4 | artifacts.require("ChallengeRegistry") 5 | ]; 6 | 7 | module.exports = (deployer, network) => { 8 | deployer.then(async () => { 9 | for (const artifact of ARTIFACTS) { 10 | const instance = await deployer.deploy(artifact); 11 | if (!tdr.isDryRunNetworkName(network)) { 12 | await tdr.appendInstance(instance); 13 | } 14 | } 15 | }); 16 | }; 17 | -------------------------------------------------------------------------------- /packages/cf-adjudicator-contracts/networks/1.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "contractName": "Migrations", 4 | "address": "0x5c83b47119f29F7A0e637F79cb1829D0c58a4615", 5 | "transactionHash": "0x3c4905f5da1bade244f028b41fce9af92f8670be3b385d8688ef3c536b1965a5" 6 | }, 7 | { 8 | "contractName": "ChallengeRegistry", 9 | "address": "0x051c7A0Bbf63Da34a612f8703f822fe2F9FA6325", 10 | "transactionHash": "0xbef160aa3a0f0348f86e602460aa017a7b37a2712d4612949b34b334a580980c" 11 | } 12 | ] -------------------------------------------------------------------------------- /packages/cf-adjudicator-contracts/networks/3.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "contractName": "Migrations", 4 | "address": "0x9a2AdA593637c7f404C3DA69Ee26e4B1E8CfC0Fd", 5 | "transactionHash": "0x193ff8806774539e875b16d80ac9f0f7b9d3d3027786d41d09ed30059ccda03c" 6 | }, 7 | { 8 | "contractName": "ChallengeRegistry", 9 | "address": "0x100Db874AC77dc8ddad7421b6464e188A2F533D6", 10 | "transactionHash": "0xbc059062b5a957c7cc75bfdccb4a4758e3ac5ff093a11652caa171b2a0f207bd" 11 | } 12 | ] -------------------------------------------------------------------------------- /packages/cf-adjudicator-contracts/networks/4.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "contractName": "Migrations", 4 | "address": "0x8793a520a67865BC68Aa1B6E0Aa4fb8797903f44", 5 | "transactionHash": "0x2851a3e2b1b791eec5839967a47df16ad11a6293362d3856425dde20ca6b64be" 6 | }, 7 | { 8 | "contractName": "ChallengeRegistry", 9 | "address": "0xfF532Ae788D2309ad436dc7edD1647CA5fd573ef", 10 | "transactionHash": "0x6894b8473436106a158bcc59492d8e0fec23acbfa3b84013b026edbf40b42ad5" 11 | } 12 | ] -------------------------------------------------------------------------------- /packages/cf-adjudicator-contracts/networks/42.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "contractName": "Migrations", 4 | "address": "0x49806390431d75b0E028cC9291909710B25F9F09", 5 | "transactionHash": "0x9ae9e17f39221b9021358e64a702d2fcedd88ef1f20d54e26a0ed08994752445" 6 | }, 7 | { 8 | "contractName": "ChallengeRegistry", 9 | "address": "0xBa9194E76AA2eF962BF56ab019B3A97A7164Bc5A", 10 | "transactionHash": "0xb4761f917ec42b82cc00aa7b4f172219314ecc5474f3a8a5077e53d1df2a8700" 11 | } 12 | ] -------------------------------------------------------------------------------- /packages/cf-adjudicator-contracts/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "module": "commonjs", 5 | "noImplicitAny": true 6 | }, 7 | "include": ["test"] 8 | } 9 | -------------------------------------------------------------------------------- /packages/cf-adjudicator-contracts/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../../tslint.json"] 3 | } 4 | -------------------------------------------------------------------------------- /packages/cf-adjudicator-contracts/waffle.js: -------------------------------------------------------------------------------- 1 | const selectSolc = require("../../waffle.js"); 2 | 3 | module.exports = selectSolc(); 4 | -------------------------------------------------------------------------------- /packages/cf-funding-protocol-contracts/.env.example: -------------------------------------------------------------------------------- 1 | ETH_ACCOUNT_MNENOMIC="some mnemonic" 2 | INFURA_API_KEY="" 3 | DEFAULT_GAS=7000000 4 | NATIVE_SOLC=true 5 | -------------------------------------------------------------------------------- /packages/cf-funding-protocol-contracts/.soliumignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | coverageEnv 3 | contracts/interpreters/MultiAssetMultiPartyCoinTransferInterpreter.sol 4 | contracts/interpreters/SwapInterpreter.sol 5 | -------------------------------------------------------------------------------- /packages/cf-funding-protocol-contracts/.soliumrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "solium:all", 3 | "plugins": ["security"], 4 | "rules": { 5 | "arg-overflow": ["error", 8], 6 | "array-declarations": 1, 7 | "camelcase": 1, 8 | "deprecated-suicide": 1, 9 | "imports-on-top": 1, 10 | "indentation": ["error", 2], 11 | "lbrace": 0, 12 | "max-len": ["error", 80], 13 | "mixedcase": 0, 14 | "no-empty-blocks": 1, 15 | "no-experimental": 0, 16 | "no-unused-vars": 1, 17 | "operator-whitespace": 1, 18 | "pragma-on-top": 1, 19 | "quotes": 1, 20 | "security/enforce-explicit-visibility": ["error"], 21 | "security/no-block-members": ["warning"], 22 | "security/no-inline-assembly": 0, 23 | "security/no-low-level-calls": 0, 24 | "uppercase": 1, 25 | "variable-declarations": 1, 26 | "whitespace": 1 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /packages/cf-funding-protocol-contracts/README.md: -------------------------------------------------------------------------------- 1 | # Updating the Contracts 2 | 3 | When any of the contracts get updated, their corresponding _expected_ build artifacts need to be updated as well. 4 | 5 | To do make this update, run: 6 | 7 | ```shell 8 | yarn build 9 | ``` 10 | -------------------------------------------------------------------------------- /packages/cf-funding-protocol-contracts/contracts/Interpreter.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.5.12; 2 | pragma experimental "ABIEncoderV2"; 3 | 4 | 5 | contract Interpreter { 6 | function interpretOutcomeAndExecuteEffect( 7 | bytes calldata, 8 | bytes calldata 9 | ) 10 | external; 11 | } 12 | -------------------------------------------------------------------------------- /packages/cf-funding-protocol-contracts/contracts/Migrations.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.5.12; 2 | 3 | 4 | contract Migrations { 5 | address public owner; 6 | uint public last_completed_migration; 7 | 8 | modifier restricted() { 9 | if (msg.sender == owner) 10 | _; 11 | } 12 | 13 | constructor() public { 14 | owner = msg.sender; 15 | } 16 | 17 | function setCompleted(uint completed) public restricted { 18 | last_completed_migration = completed; 19 | } 20 | 21 | function upgrade(address new_address) public restricted { 22 | Migrations upgraded = Migrations(new_address); 23 | upgraded.setCompleted(last_completed_migration); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /packages/cf-funding-protocol-contracts/contracts/default-apps/FinalizedApp.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.5.12; 2 | pragma experimental "ABIEncoderV2"; 3 | 4 | import "./IdentityApp.sol"; 5 | 6 | 7 | contract FinalizedApp is IdentityApp { 8 | 9 | function isStateTerminal(bytes memory) 10 | public 11 | pure 12 | returns (bool) 13 | { 14 | return true; 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /packages/cf-funding-protocol-contracts/contracts/default-apps/IdentityApp.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.5.12; 2 | pragma experimental "ABIEncoderV2"; 3 | 4 | /* solium-disable-next-line */ 5 | import "@counterfactual/cf-adjudicator-contracts/contracts/interfaces/CounterfactualApp.sol"; 6 | 7 | 8 | contract IdentityApp is CounterfactualApp { 9 | 10 | function computeOutcome(bytes memory encodedState) 11 | public 12 | pure 13 | returns (bytes memory) 14 | { 15 | return encodedState; 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /packages/cf-funding-protocol-contracts/contracts/libs/LibOutcome.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.5.12; 2 | pragma experimental "ABIEncoderV2"; 3 | 4 | 5 | library LibOutcome { 6 | 7 | struct CoinTransfer { 8 | address payable to; 9 | uint256 amount; 10 | } 11 | 12 | enum TwoPartyFixedOutcome { 13 | SEND_TO_ADDR_ONE, 14 | SEND_TO_ADDR_TWO, 15 | SPLIT_AND_SEND_TO_BOTH_ADDRS 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /packages/cf-funding-protocol-contracts/contracts/test-fixtures/DelegateProxy.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.5.12; 2 | 3 | 4 | contract DelegateProxy { 5 | function () external payable { } 6 | function delegate(address to, bytes memory data) public { 7 | (bool success, ) = to.delegatecall(data); 8 | require(success, "Delegate call failed."); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /packages/cf-funding-protocol-contracts/contracts/test-fixtures/DolphinCoin.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.5.12; 2 | 3 | import "openzeppelin-solidity/contracts/token/ERC20/ERC20.sol"; 4 | 5 | 6 | contract DolphinCoin is ERC20 { 7 | uint8 public constant DECIMALS = 18; 8 | uint256 public constant INITIAL_SUPPLY = 10000 * (10 ** uint256(DECIMALS)); 9 | 10 | /** 11 | * @dev Constructor that gives msg.sender all of existing tokens. 12 | */ 13 | constructor() public { 14 | _mint(msg.sender, INITIAL_SUPPLY); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/cf-funding-protocol-contracts/contracts/test-fixtures/Echo.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.5.12; 2 | 3 | 4 | contract Echo { 5 | address masterCopy; 6 | 7 | function helloWorld() external pure returns (string memory) { 8 | return "hello world"; 9 | } 10 | 11 | function helloWorldArg(string calldata arg) 12 | external 13 | pure 14 | returns (string memory) 15 | { 16 | return arg; 17 | } 18 | 19 | function msgSender() external view returns (address) { 20 | return msg.sender; 21 | } 22 | 23 | function returnArg(bool arg) external pure returns (bool) { 24 | return arg; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /packages/cf-funding-protocol-contracts/contracts/test-fixtures/FixedTwoPartyOutcomeApp.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.5.12; 2 | pragma experimental "ABIEncoderV2"; 3 | 4 | import "../libs/LibOutcome.sol"; 5 | 6 | 7 | contract TwoPartyFixedOutcomeApp { 8 | 9 | function computeOutcome(bytes memory) 10 | public 11 | pure 12 | returns (bytes memory) 13 | { 14 | return abi.encode( 15 | LibOutcome.TwoPartyFixedOutcome.SPLIT_AND_SEND_TO_BOTH_ADDRS 16 | ); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /packages/cf-funding-protocol-contracts/expected-build-artifacts/ECDSA.json: -------------------------------------------------------------------------------- 1 | { 2 | "abi": [], 3 | "evm": { 4 | "bytecode": { 5 | "linkReferences": {}, 6 | "object": "60556023600b82828239805160001a607314601657fe5b30600052607381538281f3fe73000000000000000000000000000000000000000030146080604052600080fdfea265627a7a723158206b32ed53f797bd8cf556e4627a0b7428ba4bb302f0858e5a16cdbc34b58616e964736f6c634300050c0032", 7 | "opcodes": "PUSH1 0x55 PUSH1 0x23 PUSH1 0xB DUP3 DUP3 DUP3 CODECOPY DUP1 MLOAD PUSH1 0x0 BYTE PUSH1 0x73 EQ PUSH1 0x16 JUMPI INVALID JUMPDEST ADDRESS PUSH1 0x0 MSTORE PUSH1 0x73 DUP2 MSTORE8 DUP3 DUP2 RETURN INVALID PUSH20 0x0 ADDRESS EQ PUSH1 0x80 PUSH1 0x40 MSTORE PUSH1 0x0 DUP1 REVERT INVALID LOG2 PUSH6 0x627A7A723158 KECCAK256 PUSH12 0x32ED53F797BD8CF556E4627A SIGNEXTEND PUSH21 0x28BA4BB302F0858E5A16CDBC34B58616E964736F6C PUSH4 0x4300050C STOP ORIGIN ", 8 | "sourceMap": "231:3422:32:-;;132:2:-1;166:7;155:9;146:7;137:37;255:7;249:14;246:1;241:23;235:4;232:33;222:2;;269:9;222:2;293:9;290:1;283:20;323:4;314:7;306:22;347:7;338;331:24" 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /packages/cf-funding-protocol-contracts/expected-build-artifacts/Interpreter.json: -------------------------------------------------------------------------------- 1 | { 2 | "abi": [ 3 | { 4 | "constant": false, 5 | "inputs": [ 6 | { 7 | "internalType": "bytes", 8 | "name": "", 9 | "type": "bytes" 10 | }, 11 | { 12 | "internalType": "bytes", 13 | "name": "", 14 | "type": "bytes" 15 | } 16 | ], 17 | "name": "interpretOutcomeAndExecuteEffect", 18 | "outputs": [], 19 | "payable": false, 20 | "stateMutability": "nonpayable", 21 | "type": "function" 22 | } 23 | ], 24 | "evm": { 25 | "bytecode": { 26 | "linkReferences": {}, 27 | "object": "", 28 | "opcodes": "", 29 | "sourceMap": "" 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /packages/cf-funding-protocol-contracts/expected-build-artifacts/LibAppCaller.json: -------------------------------------------------------------------------------- 1 | { 2 | "abi": [], 3 | "evm": { 4 | "bytecode": { 5 | "linkReferences": {}, 6 | "object": "6080604052348015600f57600080fd5b50604c80601d6000396000f3fe6080604052600080fdfea365627a7a7231582029c0648a60ad566a67407ba7398c3a8164260880a742d70f99015ef138d401986c6578706572696d656e74616cf564736f6c634300050c0040", 7 | "opcodes": "PUSH1 0x80 PUSH1 0x40 MSTORE CALLVALUE DUP1 ISZERO PUSH1 0xF JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x4C DUP1 PUSH1 0x1D PUSH1 0x0 CODECOPY PUSH1 0x0 RETURN INVALID PUSH1 0x80 PUSH1 0x40 MSTORE PUSH1 0x0 DUP1 REVERT INVALID LOG3 PUSH6 0x627A7A723158 KECCAK256 0x29 0xc0 PUSH5 0x8A60AD566A PUSH8 0x407BA7398C3A8164 0x26 ADDMOD DUP1 0xa7 TIMESTAMP 0xd7 0xf SWAP10 ADD 0x5e CALL CODESIZE 0xd4 ADD SWAP9 PUSH13 0x6578706572696D656E74616CF5 PUSH5 0x736F6C6343 STOP SDIV 0xc STOP BLOCKHASH ", 8 | "sourceMap": "248:2123:2:-;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;248:2123:2;;;;;;;" 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /packages/cf-funding-protocol-contracts/expected-build-artifacts/LibOutcome.json: -------------------------------------------------------------------------------- 1 | { 2 | "abi": [], 3 | "evm": { 4 | "bytecode": { 5 | "linkReferences": {}, 6 | "object": "60636023600b82828239805160001a607314601657fe5b30600052607381538281f3fe73000000000000000000000000000000000000000030146080604052600080fdfea365627a7a723158200014c9d116f2ae6ac540482790932ab63a4248cc442e316dc1530596bce3f74a6c6578706572696d656e74616cf564736f6c634300050c0040", 7 | "opcodes": "PUSH1 0x63 PUSH1 0x23 PUSH1 0xB DUP3 DUP3 DUP3 CODECOPY DUP1 MLOAD PUSH1 0x0 BYTE PUSH1 0x73 EQ PUSH1 0x16 JUMPI INVALID JUMPDEST ADDRESS PUSH1 0x0 MSTORE PUSH1 0x73 DUP2 MSTORE8 DUP3 DUP2 RETURN INVALID PUSH20 0x0 ADDRESS EQ PUSH1 0x80 PUSH1 0x40 MSTORE PUSH1 0x0 DUP1 REVERT INVALID LOG3 PUSH6 0x627A7A723158 KECCAK256 STOP EQ 0xc9 0xd1 AND CALLCODE 0xae PUSH11 0xC540482790932AB63A4248 0xcc DIFFICULTY 0x2e BALANCE PUSH14 0xC1530596BCE3F74A6C6578706572 PUSH10 0x6D656E74616CF564736F PUSH13 0x634300050C0040000000000000 ", 8 | "sourceMap": "62:208:24:-;;132:2:-1;166:7;155:9;146:7;137:37;255:7;249:14;246:1;241:23;235:4;232:33;222:2;;269:9;222:2;293:9;290:1;283:20;323:4;314:7;306:22;347:7;338;331:24" 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /packages/cf-funding-protocol-contracts/expected-build-artifacts/SafeMath.json: -------------------------------------------------------------------------------- 1 | { 2 | "abi": [], 3 | "evm": { 4 | "bytecode": { 5 | "linkReferences": {}, 6 | "object": "60556023600b82828239805160001a607314601657fe5b30600052607381538281f3fe73000000000000000000000000000000000000000030146080604052600080fdfea265627a7a72315820afd17dd44ee66f15691980af06b957c9083494fd0b4dce655636b221bd8feb7164736f6c634300050c0032", 7 | "opcodes": "PUSH1 0x55 PUSH1 0x23 PUSH1 0xB DUP3 DUP3 DUP3 CODECOPY DUP1 MLOAD PUSH1 0x0 BYTE PUSH1 0x73 EQ PUSH1 0x16 JUMPI INVALID JUMPDEST ADDRESS PUSH1 0x0 MSTORE PUSH1 0x73 DUP2 MSTORE8 DUP3 DUP2 RETURN INVALID PUSH20 0x0 ADDRESS EQ PUSH1 0x80 PUSH1 0x40 MSTORE PUSH1 0x0 DUP1 REVERT INVALID LOG2 PUSH6 0x627A7A723158 KECCAK256 0xaf 0xd1 PUSH30 0xD44EE66F15691980AF06B957C9083494FD0B4DCE655636B221BD8FEB7164 PUSH20 0x6F6C634300050C00320000000000000000000000 ", 8 | "sourceMap": "589:2938:33:-;;132:2:-1;166:7;155:9;146:7;137:37;255:7;249:14;246:1;241:23;235:4;232:33;222:2;;269:9;222:2;293:9;290:1;283:20;323:4;314:7;306:22;347:7;338;331:24" 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /packages/cf-funding-protocol-contracts/manual-deploy.js: -------------------------------------------------------------------------------- 1 | const ProxyFactory = require("./build/ProxyFactory.json"); 2 | 3 | const ethers = require("ethers"); 4 | 5 | require("dotenv-safe").config(); 6 | 7 | const ContractFactory = ethers.ContractFactory 8 | const Wallet = ethers.Wallet; 9 | const InfuraProvider = ethers.providers.InfuraProvider; 10 | 11 | const provider = new InfuraProvider("mainnet", process.env.INFURA_API_KEY); 12 | const wallet = Wallet.fromMnemonic(process.env.ETH_ACCOUNT_MNENOMIC).connect(provider); 13 | 14 | new ContractFactory(ProxyFactory.abi, ProxyFactory.evm.bytecode.object, wallet) 15 | .deploy({ gasLimit: 1500000 }) 16 | .then(tx => { 17 | tx.deployed() 18 | .then(contract => 19 | console.info(JSON.stringify(contract, null, 2)) 20 | ) 21 | .catch(console.error) 22 | }) 23 | .catch(console.error); 24 | -------------------------------------------------------------------------------- /packages/cf-funding-protocol-contracts/migrations/1_initial_migration.js: -------------------------------------------------------------------------------- 1 | const tdr = require("truffle-deploy-registry"); 2 | 3 | const Migrations = artifacts.require("./Migrations.sol"); 4 | 5 | module.exports = function(deployer, network) { 6 | 7 | const addToNetworkFile = (obj) => 8 | tdr.isDryRunNetworkName(network) || tdr.appendInstance(obj); 9 | 10 | deployer.deploy(Migrations).then(addToNetworkFile); 11 | 12 | }; 13 | -------------------------------------------------------------------------------- /packages/cf-funding-protocol-contracts/migrations/2_deploy_contracts.js: -------------------------------------------------------------------------------- 1 | const tdr = require("truffle-deploy-registry"); 2 | 3 | // TODO: Handle this deployment manually 4 | // artifacts.require("ProxyFactory") 5 | // Do not add to ARTIFACTS 6 | 7 | const ARTIFACTS = [ 8 | artifacts.require("ChallengeRegistry"), 9 | artifacts.require("CoinBalanceRefundApp"), 10 | artifacts.require("ConditionalTransactionDelegateTarget"), 11 | artifacts.require("IdentityApp"), 12 | artifacts.require("MinimumViableMultisig"), 13 | artifacts.require("MultiAssetMultiPartyCoinTransferInterpreter"), 14 | artifacts.require("SingleAssetTwoPartyCoinTransferFromVirtualAppInterpreter"), 15 | artifacts.require("SingleAssetTwoPartyCoinTransferInterpreter"), 16 | artifacts.require("TimeLockedPassThrough"), 17 | artifacts.require("TwoPartyFixedOutcomeFromVirtualAppInterpreter"), 18 | artifacts.require("TwoPartyFixedOutcomeInterpreter"), 19 | ]; 20 | 21 | module.exports = (deployer, network) => { 22 | deployer.then(async () => { 23 | for (const artifact of ARTIFACTS) { 24 | const instance = await deployer.deploy(artifact); 25 | if (!tdr.isDryRunNetworkName(network)) { 26 | await tdr.appendInstance(instance); 27 | } 28 | } 29 | }); 30 | }; 31 | -------------------------------------------------------------------------------- /packages/cf-funding-protocol-contracts/test/erc20-example.spec.ts: -------------------------------------------------------------------------------- 1 | import DolphinCoin from "../expected-build-artifacts/DolphinCoin.json"; 2 | import * as waffle from "ethereum-waffle"; 3 | import { Contract, Wallet } from "ethers"; 4 | import { Web3Provider } from "ethers/providers"; 5 | import { bigNumberify } from "ethers/utils"; 6 | 7 | import { expect } from "./utils/index"; 8 | 9 | const DOLPHINCOIN_SUPPLY = bigNumberify(10) 10 | .pow(18) 11 | .mul(10000); 12 | 13 | describe("DolphinCoin (ERC20) can be created", () => { 14 | let provider: Web3Provider; 15 | let wallet: Wallet; 16 | let erc20: Contract; 17 | 18 | before(async () => { 19 | provider = waffle.createMockProvider(); 20 | wallet = (await waffle.getWallets(provider))[0]; 21 | erc20 = await waffle.deployContract(wallet, DolphinCoin); 22 | }); 23 | 24 | describe("Deployer has all of initial supply", () => { 25 | it("Initial supply for deployer is DOLPHINCOIN_SUPPLY", async () => { 26 | expect(await erc20.functions.balanceOf(wallet.address)).to.be.eq( 27 | DOLPHINCOIN_SUPPLY 28 | ); 29 | }); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /packages/cf-funding-protocol-contracts/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "module": "commonjs", 5 | "noImplicitAny": true 6 | }, 7 | "include": ["test"] 8 | } 9 | -------------------------------------------------------------------------------- /packages/cf-funding-protocol-contracts/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../../tslint.json"] 3 | } 4 | -------------------------------------------------------------------------------- /packages/cf-funding-protocol-contracts/waffle.js: -------------------------------------------------------------------------------- 1 | const selectSolc = require("../../waffle.js"); 2 | 3 | module.exports = selectSolc(); 4 | -------------------------------------------------------------------------------- /packages/cf-wallet.js/.gitignore: -------------------------------------------------------------------------------- 1 | jest-cache 2 | docs/ 3 | -------------------------------------------------------------------------------- /packages/cf-wallet.js/jest.config.js: -------------------------------------------------------------------------------- 1 | const rootConfig = require("../../jest.config"); 2 | 3 | module.exports = Object.assign( 4 | rootConfig, 5 | { 6 | /* Insert custom Jest configs here */ 7 | } 8 | ); 9 | -------------------------------------------------------------------------------- /packages/cf-wallet.js/rollup.config.js: -------------------------------------------------------------------------------- 1 | import typescript from "rollup-plugin-typescript2"; 2 | import nodeResolve from "rollup-plugin-node-resolve"; 3 | import commonjs from "rollup-plugin-commonjs"; 4 | 5 | import pkg from "./package.json"; 6 | 7 | const bundledDependencies = new Set([ 8 | "@counterfactual/node-provider", 9 | "@counterfactual/types", 10 | "eventemitter3", 11 | "rpc-server" 12 | ]); 13 | 14 | export default { 15 | input: "src/index.ts", 16 | output: [ 17 | { 18 | file: pkg.main, 19 | format: "cjs", 20 | exports: "named" 21 | }, 22 | { 23 | file: pkg.iife, 24 | name: "window.cfWallet", 25 | format: "iife", 26 | exports: "named", 27 | globals: { 28 | "ethers/utils": "ethers.utils", 29 | "ethers/constants": "ethers.constants" 30 | } 31 | } 32 | ], 33 | plugins: [ 34 | commonjs(), 35 | nodeResolve({ 36 | only: [...bundledDependencies] 37 | }), 38 | typescript() 39 | ] 40 | }; 41 | -------------------------------------------------------------------------------- /packages/cf-wallet.js/src/index.ts: -------------------------------------------------------------------------------- 1 | import NodeProvider from "@counterfactual/node-provider"; 2 | 3 | import { Provider } from "./provider"; 4 | import * as types from "./types"; 5 | import * as utils from "./utils"; 6 | 7 | const cfWallet = { 8 | Provider, 9 | types, 10 | utils 11 | }; 12 | 13 | export { Provider, types, utils }; 14 | 15 | export { NodeProvider }; 16 | export default cfWallet; 17 | -------------------------------------------------------------------------------- /packages/cf-wallet.js/src/types/events.ts: -------------------------------------------------------------------------------- 1 | import { AppInstance } from "../app-instance"; 2 | 3 | export enum EventType { 4 | INSTALL = "install", 5 | INSTALL_VIRTUAL = "installVirtual", 6 | REJECT_INSTALL = "rejectInstall", 7 | CREATE_CHANNEL = "createChannel", 8 | CREATE_MULTISIG = "createMultisig", 9 | ERROR = "error" 10 | } 11 | 12 | type AppEventData = { 13 | appInstance: AppInstance; 14 | }; 15 | 16 | export type InstallEventData = AppEventData; 17 | 18 | export type RejectInstallEventData = AppEventData; 19 | 20 | export type CreateMultisigEventData = { 21 | owners: string[]; 22 | multisigAddress: string; 23 | }; 24 | 25 | export type ErrorEventData = { 26 | errorName: string; 27 | message?: string; 28 | appInstanceId?: string; 29 | extra?: { [k: string]: string | number | boolean | object }; 30 | }; 31 | 32 | export type EventData = 33 | | InstallEventData 34 | | RejectInstallEventData 35 | | ErrorEventData 36 | | CreateMultisigEventData; 37 | 38 | export type CounterfactualEvent = { 39 | readonly type: EventType; 40 | readonly data: EventData; 41 | }; 42 | -------------------------------------------------------------------------------- /packages/cf-wallet.js/src/types/index.ts: -------------------------------------------------------------------------------- 1 | import { 2 | CounterfactualEvent, 3 | ErrorEventData, 4 | EventData, 5 | EventType, 6 | InstallEventData, 7 | RejectInstallEventData 8 | } from "./events"; 9 | 10 | export { 11 | CounterfactualEvent, 12 | EventType, 13 | InstallEventData, 14 | RejectInstallEventData, 15 | ErrorEventData, 16 | EventData 17 | }; 18 | -------------------------------------------------------------------------------- /packages/cf-wallet.js/src/utils/abi.ts: -------------------------------------------------------------------------------- 1 | import { Arrayish, defaultAbiCoder, solidityPack } from "ethers/utils"; 2 | 3 | /** 4 | * Returns a hex string of the values encoded as the types. 5 | * Throws if a value is invalid for the type. 6 | * 7 | * @param types Data types 8 | * @param values Values to encode 9 | */ 10 | export function encode(types: string[], values: any[]) { 11 | return defaultAbiCoder.encode(types, values); 12 | } 13 | 14 | /** 15 | * Returns an Object by parsing data assuming types, with each parameter accessible as a positional parameters. 16 | * Throws if data is invalid for the types. 17 | * 18 | * @param types Data types 19 | * @param values Values to decode 20 | */ 21 | export function decode(types: string[], data: Arrayish) { 22 | return defaultAbiCoder.decode(types, data); 23 | } 24 | 25 | /** 26 | * Compute the Solidity non-standard (tightly) packed data for values given the types. 27 | * 28 | * @param types Data types 29 | * @param values Values to pack 30 | */ 31 | export function encodePacked(types: string[], values: any[]) { 32 | return solidityPack(types, values); 33 | } 34 | -------------------------------------------------------------------------------- /packages/cf-wallet.js/src/utils/index.ts: -------------------------------------------------------------------------------- 1 | import * as abi from "./abi"; 2 | 3 | export { abi }; 4 | -------------------------------------------------------------------------------- /packages/cf-wallet.js/test/utils/abi.spec.ts: -------------------------------------------------------------------------------- 1 | import { abi } from "../../src/utils"; 2 | 3 | const encoded255 = 4 | "0x00000000000000000000000000000000000000000000000000000000000000ff"; 5 | 6 | describe("Utils / abi", () => { 7 | it("can encode values when provided a type", () => { 8 | expect(abi.encode(["uint8"], [255])).toEqual(encoded255); 9 | }); 10 | 11 | it("can decode data when provided a type", () => { 12 | expect(abi.decode(["uint8"], encoded255)).toEqual([255]); 13 | }); 14 | 15 | it("can compute the solidity packed data when provided types and values", () => { 16 | expect( 17 | abi.encodePacked(["int8", "bytes1", "string"], [-1, "0x42", "hello"]) 18 | ).toEqual("0xff4268656c6c6f"); 19 | }); 20 | }); 21 | -------------------------------------------------------------------------------- /packages/cf-wallet.js/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "declarationDir": "dist/", 5 | "outDir": "dist/", 6 | "types": ["jest"] 7 | }, 8 | "include": ["src", "test"] 9 | } 10 | -------------------------------------------------------------------------------- /packages/cf-wallet.js/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../../tslint.json"] 3 | } 4 | -------------------------------------------------------------------------------- /packages/cf-wallet.js/typedoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "mode": "file", 3 | "out": "docs" 4 | } -------------------------------------------------------------------------------- /packages/cf.js/.gitignore: -------------------------------------------------------------------------------- 1 | jest-cache 2 | docs/ 3 | -------------------------------------------------------------------------------- /packages/cf.js/jest.config.js: -------------------------------------------------------------------------------- 1 | const rootConfig = require("../../jest.config"); 2 | 3 | module.exports = Object.assign( 4 | rootConfig, 5 | { 6 | /* Insert custom Jest configs here */ 7 | } 8 | ); 9 | -------------------------------------------------------------------------------- /packages/cf.js/rollup.config.js: -------------------------------------------------------------------------------- 1 | import typescript from "rollup-plugin-typescript2"; 2 | import nodeResolve from "rollup-plugin-node-resolve"; 3 | import commonjs from "rollup-plugin-commonjs"; 4 | 5 | import pkg from "./package.json"; 6 | 7 | const bundledDependencies = new Set([ 8 | "@counterfactual/node-provider", 9 | "@counterfactual/types", 10 | "eventemitter3", 11 | "rpc-server" 12 | ]); 13 | 14 | export default { 15 | input: "src/index.ts", 16 | output: [ 17 | { 18 | file: pkg.main, 19 | format: "cjs", 20 | exports: "named" 21 | }, 22 | { 23 | file: pkg.iife, 24 | name: "window.cf", 25 | format: "iife", 26 | exports: "named", 27 | globals: { 28 | "ethers/utils": "ethers.utils", 29 | "ethers/constants": "ethers.constants" 30 | } 31 | } 32 | ], 33 | plugins: [ 34 | commonjs(), 35 | nodeResolve({ 36 | only: [...bundledDependencies] 37 | }), 38 | typescript(), 39 | ] 40 | }; 41 | -------------------------------------------------------------------------------- /packages/cf.js/src/index.ts: -------------------------------------------------------------------------------- 1 | import NodeProvider, { 2 | NodeProviderEthereum 3 | } from "@counterfactual/node-provider"; 4 | 5 | import { AppFactory } from "./app-factory"; 6 | import { Provider } from "./provider"; 7 | import * as types from "./types"; 8 | import * as utils from "./utils"; 9 | 10 | const cf = { 11 | AppFactory, 12 | Provider, 13 | types, 14 | utils 15 | }; 16 | 17 | export { AppFactory, Provider, types, utils }; 18 | export { NodeProvider, NodeProviderEthereum }; 19 | 20 | export default cf; 21 | -------------------------------------------------------------------------------- /packages/cf.js/src/types/index.ts: -------------------------------------------------------------------------------- 1 | import { 2 | CounterfactualEvent, 3 | ErrorEventData, 4 | EventData, 5 | EventType, 6 | InstallEventData, 7 | RejectInstallEventData, 8 | UninstallEventData, 9 | UpdateStateEventData 10 | } from "./events"; 11 | 12 | export { 13 | CounterfactualEvent, 14 | EventType, 15 | InstallEventData, 16 | RejectInstallEventData, 17 | UninstallEventData, 18 | UpdateStateEventData, 19 | ErrorEventData, 20 | EventData 21 | }; 22 | -------------------------------------------------------------------------------- /packages/cf.js/src/utils/abi.ts: -------------------------------------------------------------------------------- 1 | import { Arrayish, defaultAbiCoder, solidityPack } from "ethers/utils"; 2 | 3 | export function encode(types: string[], values: any[]) { 4 | return defaultAbiCoder.encode(types, values); 5 | } 6 | 7 | export function decode(types: string[], data: Arrayish) { 8 | return defaultAbiCoder.decode(types, data); 9 | } 10 | 11 | export function encodePacked(types: string[], values: any[]) { 12 | return solidityPack(types, values); 13 | } 14 | -------------------------------------------------------------------------------- /packages/cf.js/src/utils/index.ts: -------------------------------------------------------------------------------- 1 | import * as abi from "./abi"; 2 | 3 | export { abi }; 4 | -------------------------------------------------------------------------------- /packages/cf.js/test/utils/abi.spec.ts: -------------------------------------------------------------------------------- 1 | import { abi } from "../../src/utils"; 2 | 3 | const encoded255 = 4 | "0x00000000000000000000000000000000000000000000000000000000000000ff"; 5 | 6 | describe("Utils / abi", () => { 7 | it("can encode values when provided a type", () => { 8 | expect(abi.encode(["uint8"], [255])).toEqual(encoded255); 9 | }); 10 | 11 | it("can decode data when provided a type", () => { 12 | expect(abi.decode(["uint8"], encoded255)).toEqual([255]); 13 | }); 14 | 15 | it("can compute the solidity packed data when provided types and values", () => { 16 | expect( 17 | abi.encodePacked(["int8", "bytes1", "string"], [-1, "0x42", "hello"]) 18 | ).toEqual("0xff4268656c6c6f"); 19 | }); 20 | }); 21 | -------------------------------------------------------------------------------- /packages/cf.js/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "sourceRoot": ".", 5 | "declarationDir": "dist/", 6 | "outDir": "dist/", 7 | "types": ["jest"] 8 | }, 9 | "references": [ 10 | { "path": "../types" } 11 | ], 12 | "include": ["src", "test"] 13 | } 14 | -------------------------------------------------------------------------------- /packages/cf.js/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../../tslint.json"] 3 | } 4 | -------------------------------------------------------------------------------- /packages/cf.js/typedoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "mode": "file", 3 | "out": "docs" 4 | } -------------------------------------------------------------------------------- /packages/firebase-client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@counterfactual/firebase-client", 3 | "version": "0.0.7", 4 | "main": "dist/index.js", 5 | "iife": "dist/index.iife.js", 6 | "types": "dist/index.d.ts", 7 | "license": "MIT", 8 | "engines": { 9 | "yarn": ">=1.17.3", 10 | "node": "^10 || ^12" 11 | }, 12 | "scripts": { 13 | "build": "tsc -b . && rollup -c", 14 | "build:watch": "tsc -b . && rollup -c -w", 15 | "lint:fix": "tslint -c tslint.json -p . --fix", 16 | "lint": "tslint -c tslint.json -p ." 17 | }, 18 | "devDependencies": { 19 | "@counterfactual/types": "0.0.45", 20 | "@firebase/app-types": "0.4.6", 21 | "@firebase/util": "0.2.31", 22 | "@types/firebase": "3.2.1", 23 | "rollup": "1.26.0", 24 | "typescript": "3.5.3" 25 | }, 26 | "dependencies": { 27 | "firebase": "6.0.2" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /packages/firebase-client/rollup.config.js: -------------------------------------------------------------------------------- 1 | import typescript from "rollup-plugin-typescript2"; 2 | 3 | import pkg from "./package.json"; 4 | 5 | const globals = { 6 | firebase: "firebase", 7 | loglevel: "log" 8 | }; 9 | 10 | export default [ 11 | { 12 | input: "src/index.ts", 13 | output: [ 14 | { 15 | file: pkg.main, 16 | sourcemap: true, 17 | format: "cjs" 18 | }, 19 | { 20 | file: pkg.iife, 21 | sourcemap: true, 22 | name: "window", 23 | extend: true, 24 | format: "iife", 25 | globals: globals 26 | } 27 | ], 28 | plugins: [typescript()] 29 | } 30 | ]; 31 | -------------------------------------------------------------------------------- /packages/firebase-client/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "declaration": true, 5 | "declarationDir": "dist/", 6 | "outDir": "dist/", 7 | "resolveJsonModule": true, 8 | "typeRoots": [ 9 | "../../node_modules/@types", 10 | "../../node_modules/@counterfactual/typescript-typings/types" 11 | ] 12 | }, 13 | "include": ["src"] 14 | } 15 | -------------------------------------------------------------------------------- /packages/firebase-client/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../../tslint.json"] 3 | } 4 | -------------------------------------------------------------------------------- /packages/firebase-server/.env.defaults: -------------------------------------------------------------------------------- 1 | FIREBASE_DEV_SERVER_HOST="localhost" 2 | FIREBASE_DEV_SERVER_PORT=9999 3 | FIREBASE_MESSAGING_SERVER_KEY="messageKey" 4 | FIREBASE_STORE_SERVER_KEY="storeKey" 5 | FIREBASE_STORE_PREFIX_KEY="dev-store" 6 | GANACHE_PORT="8545" 7 | -------------------------------------------------------------------------------- /packages/firebase-server/.env.example: -------------------------------------------------------------------------------- 1 | # Values used for test suite 2 | DEV_GANACHE_HOST= 3 | DEV_GANACHE_PORT= 4 | DEV_GANACHE_MNEMONIC= 5 | DEV_GANACHE_NETWORK_ID= 6 | -------------------------------------------------------------------------------- /packages/firebase-server/.env.schema: -------------------------------------------------------------------------------- 1 | FIREBASE_DEV_SERVER_HOST= 2 | FIREBASE_DEV_SERVER_PORT= 3 | FIREBASE_MESSAGING_SERVER_KEY= 4 | FIREBASE_STORE_SERVER_KEY= 5 | FIREBASE_STORE_PREFIX_KEY= 6 | GANACHE_PORT= 7 | -------------------------------------------------------------------------------- /packages/firebase-server/.gitignore: -------------------------------------------------------------------------------- 1 | jest-cache 2 | jest-coverage/clover.xml 3 | jest-coverage/coverage-final.json 4 | jest-coverage/lcov-report/ 5 | jest-coverage/lcov.info 6 | .env 7 | networks/ 8 | -------------------------------------------------------------------------------- /packages/firebase-server/jest.config.js: -------------------------------------------------------------------------------- 1 | const rootConfig = require("../../jest.config"); 2 | 3 | module.exports = Object.assign( 4 | rootConfig, 5 | { 6 | /* Insert custom Jest configs here */ 7 | } 8 | ); 9 | -------------------------------------------------------------------------------- /packages/firebase-server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@counterfactual/firebase-server", 3 | "version": "0.0.5", 4 | "main": "dist/index.js", 5 | "types": "dist/src/index.d.ts", 6 | "license": "MIT", 7 | "engines": { 8 | "yarn": ">=1.17.3", 9 | "node": "^10 || ^12" 10 | }, 11 | "scripts": { 12 | "build": "tsc -b . && rollup -c", 13 | "build:watch": "tsc -b . && rollup -c -w", 14 | "test": "jest --setupFiles dotenv-extended/config --runInBand --bail --forceExit", 15 | "test:coverage": "jest --runInBand --detectOpenHandles --bail --coverage", 16 | "lint:fix": "tslint -c tslint.json -p . --fix", 17 | "lint": "tslint -c tslint.json -p ." 18 | }, 19 | "devDependencies": { 20 | "@counterfactual/firebase-client": "0.0.7", 21 | "@firebase/app-types": "0.4.6", 22 | "@firebase/util": "0.2.31", 23 | "@types/jest": "24.0.15", 24 | "firebase-server": "1.0.2", 25 | "jest": "24.8.0", 26 | "uuid": "3.3.3" 27 | }, 28 | "dependencies": { 29 | "firebase": "6.0.2" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /packages/firebase-server/rollup.config.js: -------------------------------------------------------------------------------- 1 | import typescript from "rollup-plugin-typescript2"; 2 | 3 | import pkg from "./package.json"; 4 | 5 | export default [ 6 | { 7 | input: "src/index.ts", 8 | output: [ 9 | { 10 | file: pkg.main, 11 | sourcemap: true, 12 | format: "cjs" 13 | } 14 | ], 15 | plugins: [ 16 | typescript() 17 | ] 18 | } 19 | ]; 20 | -------------------------------------------------------------------------------- /packages/firebase-server/src/index.ts: -------------------------------------------------------------------------------- 1 | import { 2 | EMPTY_FIREBASE_CONFIG, 3 | FirebaseServiceFactory 4 | } from "@counterfactual/firebase-client"; 5 | import FirebaseServer from "firebase-server"; 6 | 7 | /** 8 | * This wraps the FirebaseServiceFactory while providing a Firebase Server 9 | * for the client to connect to. 10 | */ 11 | export class LocalFirebaseServiceFactory extends FirebaseServiceFactory { 12 | firebaseServer: FirebaseServer; 13 | constructor(private readonly host: string, private readonly port: string) { 14 | super({ 15 | ...EMPTY_FIREBASE_CONFIG, 16 | databaseURL: `ws://${host}:${port}` 17 | }); 18 | 19 | this.firebaseServer = new FirebaseServer(this.port, this.host); 20 | } 21 | 22 | async closeServiceConnections() { 23 | await this.firebaseServer.close(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /packages/firebase-server/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "declaration": true, 5 | "declarationDir": "dist/", 6 | "outDir": "dist/", 7 | "resolveJsonModule": true, 8 | "typeRoots": [ 9 | "../../node_modules/@types", 10 | "../../node_modules/@counterfactual/typescript-typings/types" 11 | ], 12 | "types": ["jest"] 13 | }, 14 | "include": ["src", "test"] 15 | } 16 | -------------------------------------------------------------------------------- /packages/firebase-server/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../../tslint.json"] 3 | } 4 | -------------------------------------------------------------------------------- /packages/greenboard/.gitignore: -------------------------------------------------------------------------------- 1 | extension 2 | chrome-profile 3 | -------------------------------------------------------------------------------- /packages/greenboard/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "typeRoots": ["../../node_modules/@types"] 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /packages/greenboard/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../../tslint.json"], 3 | "rules": { 4 | "ordered-imports": false 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /packages/greenboard/utils/chrome-selectors.ts: -------------------------------------------------------------------------------- 1 | import { By, promise, WebDriver } from "selenium-webdriver"; 2 | 3 | export const EXTENSION_LIST_SELECTOR = By.id("extensions-list"); 4 | 5 | export const METAMASK_EXTENSION_URL_SELECTOR = async (driver: WebDriver) => { 6 | const names = await driver.findElements(By.className("name")); 7 | const urls = await driver.findElements(By.className("url")); 8 | 9 | return promise.filter(urls, async (element, index) => { 10 | return ( 11 | (await names[index].getText()) === "MetaMask" && 12 | (await element.getText()).startsWith("chrome-extension") 13 | ); 14 | }); 15 | }; 16 | -------------------------------------------------------------------------------- /packages/greenboard/utils/counterfactual-wallet-selectors.ts: -------------------------------------------------------------------------------- 1 | import { By } from "selenium-webdriver"; 2 | 3 | export const WELCOME_SCREEN_SELECTORS = { 4 | setupCounterfactualButton: By.css("[data-test-selector='setup-button']") 5 | }; 6 | 7 | export const ACCOUNT_REGISTRATION_SELECTORS = { 8 | usernameInput: By.css("[data-test-selector='username-input']"), 9 | emailInput: By.css("[data-test-selector='email-input']"), 10 | createAccountButton: By.css("[data-test-selector='register-button']") 11 | }; 12 | 13 | export const ACCOUNT_DEPOSIT_SELECTORS = { 14 | formTitle: By.css(".widget-header"), 15 | amountInput: By.css("[data-test-selector='amount-input']"), 16 | proceedButton: By.css("[data-test-selector='deposit-button']") 17 | }; 18 | 19 | export const LAYOUT_HEADER_SELECTORS = { 20 | loginButton: By.css("[data-test-selector='login-button']"), 21 | logoContainer: By.css(".header > .header-content > .logo-container"), 22 | accountContainer: By.css("header > .header-content .account-container"), 23 | userNameText: By.css("[data-test-selector='info-user'] .info-content"), 24 | balanceText: By.css("[data-test-selector='info-balance'] .info-content") 25 | }; 26 | -------------------------------------------------------------------------------- /packages/greenboard/utils/test-sequencer.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Use this array to set the order of execution for all E2E tests. 3 | * 4 | * IMPORTANT: 5 | * Keep in mind that if you create a new scenario, you'll need to include it 6 | * on this list, otherwise it won't be executed. 7 | */ 8 | const testSequence = ["tests/onboarding.spec.ts", "tests/login.spec.ts"]; 9 | 10 | // Import the default test sequencer. 11 | const Sequencer = require("@jest/test-sequencer").default; 12 | 13 | module.exports = class extends Sequencer { 14 | /** 15 | * Sorts the tests by order of appearance in the `testSequence` array. 16 | */ 17 | sort(tests) { 18 | return testSequence.map(testName => 19 | tests.find(test => test.path.endsWith(testName)) 20 | ); 21 | } 22 | }; 23 | -------------------------------------------------------------------------------- /packages/greenboard/utils/types.ts: -------------------------------------------------------------------------------- 1 | export type MetamaskOptions = { 2 | seedPhraseValue: string; 3 | passwordValue: string; 4 | }; 5 | 6 | export type MetamaskNetwork = 7 | | "main" 8 | | "ropsten" 9 | | "kovan" 10 | | "rinkeby" 11 | | "goerli" 12 | | "localhost" 13 | | "custom"; 14 | 15 | export type MetamaskNotification = "requestSignature"; 16 | 17 | export type MetamaskTransaction = "signatureRequest" | "deposit"; 18 | 19 | export const enum MetamaskFlowType { 20 | NewUser, 21 | ReturningUser 22 | } 23 | 24 | export const enum TestBrowserContext { 25 | MetamaskMain = "metamask:main", 26 | MetamaskPopup = "metamask:popup", 27 | CounterfactualWallet = "counterfactual:wallet" 28 | } 29 | 30 | export const enum CounterfactualScreenName { 31 | Welcome = "welcome", 32 | OnboardingRegistration = "onboarding:register", 33 | OnboardingDeposit = "onboarding:deposit", 34 | Channels = "channels" 35 | } 36 | 37 | export type StringHashMap = { [key: string]: string }; 38 | -------------------------------------------------------------------------------- /packages/local-ganache-server/.env.defaults: -------------------------------------------------------------------------------- 1 | GANACHE_PORT=8545 2 | -------------------------------------------------------------------------------- /packages/local-ganache-server/.gitignore: -------------------------------------------------------------------------------- 1 | jest-cache 2 | jest-coverage/clover.xml 3 | jest-coverage/coverage-final.json 4 | jest-coverage/lcov-report/ 5 | jest-coverage/lcov.info 6 | .env 7 | -------------------------------------------------------------------------------- /packages/local-ganache-server/README.md: -------------------------------------------------------------------------------- 1 | # [@counterfactual/local-ganache-server](https://github.com/counterfactual/monorepo/tree/master/packages/local-ganache-server) 2 | 3 | This package encapsulates the minimum necessary blockchain context that needs to be setup for the `@counterfactual/node` package to function as intended. 4 | 5 | This package is primarily for testing purposes (specifically makes it easier to test packages that _consume_ the `Node` package and need the sufficient blockchain context to feed into the `Node`). 6 | -------------------------------------------------------------------------------- /packages/local-ganache-server/jest.config.js: -------------------------------------------------------------------------------- 1 | const rootConfig = require("../../jest.config"); 2 | 3 | module.exports = Object.assign( 4 | rootConfig, 5 | { 6 | /* Insert custom Jest configs here */ 7 | } 8 | ); 9 | -------------------------------------------------------------------------------- /packages/local-ganache-server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@counterfactual/local-ganache-server", 3 | "version": "0.0.10", 4 | "description": "Utility package to spin up a blockchain with deployed contracts for testing purposes", 5 | "main": "src/index.ts", 6 | "license": "MIT", 7 | "engines": { 8 | "yarn": ">=1.17.3", 9 | "node": "^10 || ^12" 10 | }, 11 | "scripts": { 12 | "build": "tsc -b .", 13 | "test": "jest --setupFiles dotenv-extended/config --runInBand --bail", 14 | "test:coverage": "jest --runInBand --detectOpenHandles --bail --coverage", 15 | "lint:fix": "tslint -c tslint.json -p . --fix", 16 | "lint": "tslint -c tslint.json -p ." 17 | }, 18 | "devDependencies": { 19 | "typescript": "3.5.3" 20 | }, 21 | "dependencies": { 22 | "@counterfactual/apps": "0.1.14", 23 | "@counterfactual/cf-adjudicator-contracts": "0.0.10", 24 | "@counterfactual/cf-funding-protocol-contracts": "0.0.13", 25 | "@counterfactual/types": "0.0.45", 26 | "ethers": "4.0.38", 27 | "ganache-core": "2.8.0" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /packages/local-ganache-server/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "composite": true, 5 | "outDir": "dist/", 6 | "resolveJsonModule": true, 7 | "types": ["jest"] 8 | }, 9 | "references": [ 10 | { "path": "../types" } 11 | ], 12 | "include": ["src", "test"] 13 | } 14 | -------------------------------------------------------------------------------- /packages/local-ganache-server/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../../tslint.json"] 3 | } 4 | -------------------------------------------------------------------------------- /packages/node-provider/.gitignore: -------------------------------------------------------------------------------- 1 | jest-cache 2 | -------------------------------------------------------------------------------- /packages/node-provider/jest.config.js: -------------------------------------------------------------------------------- 1 | const rootConfig = require("../../jest.config"); 2 | 3 | module.exports = Object.assign( 4 | rootConfig, 5 | { 6 | /* Insert custom Jest configs here */ 7 | } 8 | ); 9 | -------------------------------------------------------------------------------- /packages/node-provider/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@counterfactual/node-provider", 3 | "version": "0.2.3", 4 | "main": "dist/index.js", 5 | "types": "dist/src/index.d.ts", 6 | "module": "dist/index.es.js", 7 | "engines": { 8 | "yarn": ">=1.17.3", 9 | "node": "^10 || ^12" 10 | }, 11 | "files": [ 12 | "dist" 13 | ], 14 | "license": "MIT", 15 | "scripts": { 16 | "build": "tsc -b . && rollup -c", 17 | "test": "tsc -b . && jest --runInBand --detectOpenHandles --bail --forceExit", 18 | "test-debug": "node --inspect-brk jest --runInBand", 19 | "lint:fix": "tslint -c tslint.json -p . --fix", 20 | "lint": "tslint -c tslint.json -p ." 21 | }, 22 | "devDependencies": { 23 | "@counterfactual/types": "0.0.45", 24 | "@types/jest": "24.0.15", 25 | "@types/web3": "1.0.20", 26 | "jest": "24.8.0", 27 | "rollup-plugin-commonjs": "10.1.0", 28 | "rollup-plugin-node-resolve": "5.2.0", 29 | "rollup-plugin-typescript2": "0.24.3", 30 | "ts-jest": "24.0.2", 31 | "tslint": "5.20.0", 32 | "typescript": "3.5.3" 33 | }, 34 | "dependencies": { 35 | "eventemitter3": "^4.0.0" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /packages/node-provider/rollup.config.js: -------------------------------------------------------------------------------- 1 | import typescript from "rollup-plugin-typescript2"; 2 | import pkg from "./package.json"; 3 | 4 | export default [ 5 | { 6 | input: "src/index.ts", 7 | output: [ 8 | { 9 | file: pkg.main, 10 | format: "cjs" 11 | }, 12 | { 13 | file: pkg.module, 14 | format: "es", 15 | } 16 | ], 17 | external: [ 18 | ...Object.keys(pkg.peerDependencies || {}), 19 | ], 20 | plugins: [ 21 | typescript() 22 | ] 23 | } 24 | ]; 25 | -------------------------------------------------------------------------------- /packages/node-provider/src/index.ts: -------------------------------------------------------------------------------- 1 | import NodeProvider from "./node-provider"; 2 | import NodeProviderEthereum from "./node-provider-ethereum"; 3 | 4 | export { NodeProviderEthereum }; 5 | export default NodeProvider; 6 | -------------------------------------------------------------------------------- /packages/node-provider/src/types.ts: -------------------------------------------------------------------------------- 1 | import { Node } from "@counterfactual/types"; 2 | import { IpcProvider, JsonRPCResponse } from "web3/providers"; 3 | 4 | export type NodeEthereumResponse = JsonRPCResponse & { result: Node.Message }; 5 | 6 | export type EthereumGlobal = Omit & { 7 | enable: () => Promise; 8 | selectedAddress: string; 9 | networkVersion: string; 10 | send: (eventOrMethod: string, data?: any[]) => Promise; 11 | }; 12 | -------------------------------------------------------------------------------- /packages/node-provider/test/integration/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/counterfactual/monorepo/f431e3ded7ffeec2e1e2c3cf7b4eeb1854e1154a/packages/node-provider/test/integration/.gitkeep -------------------------------------------------------------------------------- /packages/node-provider/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "dist/", 5 | "noImplicitAny": true, 6 | "types": ["jest", "node", "web3"] 7 | }, 8 | "references": [{ "path": "../types" }], 9 | "include": ["src", "test"], 10 | "exclude": ["dist"] 11 | } 12 | -------------------------------------------------------------------------------- /packages/node-provider/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../../tslint.json"] 3 | } 4 | -------------------------------------------------------------------------------- /packages/node/.env.defaults: -------------------------------------------------------------------------------- 1 | FIREBASE_DEV_SERVER_HOST="localhost" 2 | FIREBASE_DEV_SERVER_PORT=9999 3 | FIREBASE_MESSAGING_SERVER_KEY="messageKey" 4 | FIREBASE_STORE_SERVER_KEY="storeKey" 5 | FIREBASE_STORE_PREFIX_KEY="dev-store" 6 | GANACHE_PORT="8545" -------------------------------------------------------------------------------- /packages/node/.env.example: -------------------------------------------------------------------------------- 1 | # Values used for test suite 2 | DEV_GANACHE_HOST= 3 | DEV_GANACHE_PORT= 4 | DEV_GANACHE_MNEMONIC= 5 | DEV_GANACHE_NETWORK_ID= 6 | -------------------------------------------------------------------------------- /packages/node/.env.schema: -------------------------------------------------------------------------------- 1 | FIREBASE_DEV_SERVER_HOST= 2 | FIREBASE_DEV_SERVER_PORT= 3 | FIREBASE_MESSAGING_SERVER_KEY= 4 | FIREBASE_STORE_SERVER_KEY= 5 | FIREBASE_STORE_PREFIX_KEY= 6 | GANACHE_PORT= 7 | -------------------------------------------------------------------------------- /packages/node/.gitignore: -------------------------------------------------------------------------------- 1 | jest-cache 2 | jest-coverage/clover.xml 3 | jest-coverage/coverage-final.json 4 | jest-coverage/lcov-report/ 5 | jest-coverage/lcov.info 6 | .env 7 | networks/ 8 | docs/venv/* 9 | -------------------------------------------------------------------------------- /packages/node/README.md: -------------------------------------------------------------------------------- 1 | https://cf-node.readthedocs.io/en/latest/ 2 | -------------------------------------------------------------------------------- /packages/node/docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | SOURCEDIR = source 8 | BUILDDIR = build 9 | 10 | # Put it first so that "make" without argument is like "make help". 11 | help: 12 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 13 | 14 | .PHONY: help Makefile 15 | 16 | # Catch-all target: route all unknown targets to Sphinx using the new 17 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 18 | %: Makefile 19 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) -------------------------------------------------------------------------------- /packages/node/docs/requirements.txt: -------------------------------------------------------------------------------- 1 | sphinx_markdown_tables 2 | sphinxcontrib-mermaid 3 | recommonmark 4 | sphinx 5 | sphinx_rtd_theme 6 | pygments-lexer-solidity 7 | -------------------------------------------------------------------------------- /packages/node/docs/source/diagram.md: -------------------------------------------------------------------------------- 1 | ## Diagrams 2 | 3 | These diagrams are available to help you understand the underlying architecture of the Node. 4 | 5 | ### Ownership 6 | > arrows indicate "has a pointer to" 7 | 8 | ```eval_rst 9 | .. mermaid:: ./diagrams/node-ownership.mmd 10 | ``` 11 | 12 | ### Control Flow 13 | > arrows mostly indicate "calls" 14 | 15 | ```eval_rst 16 | .. mermaid:: ./diagrams/node-cfg.mmd 17 | ``` -------------------------------------------------------------------------------- /packages/node/docs/source/diagrams/node-ownership.mmd: -------------------------------------------------------------------------------- 1 | graph LR 2 | subgraph Node 3 | ProtocolRunner 4 | MessagingService 5 | RpcRouter --> RequestHandler 6 | RequestHandler --> RpcRouter 7 | RequestHandler --> StoreService 8 | RequestHandler --> MessagingService 9 | RequestHandler --> ProtocolRunner 10 | end 11 | -------------------------------------------------------------------------------- /packages/node/docs/source/index.rst: -------------------------------------------------------------------------------- 1 | .. cf-specs documentation master file, created by 2 | sphinx-quickstart on Mon Mar 25 14:33:18 2019. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Welcome to Node documentation! 7 | ========================================== 8 | 9 | .. toctree:: 10 | :maxdepth: 2 11 | :caption: Contents: 12 | 13 | introduction.md 14 | api.md 15 | diagram.md 16 | -------------------------------------------------------------------------------- /packages/node/jest.config.js: -------------------------------------------------------------------------------- 1 | const rootConfig = require("../../jest.config"); 2 | 3 | module.exports = Object.assign( 4 | rootConfig, 5 | { 6 | "globalSetup": "/test/global-setup.jest.ts", 7 | "globalTeardown": "/test/global-teardown.jest.ts", 8 | "testEnvironment": "/test/node-test-environment.jest.js", 9 | } 10 | ); 11 | -------------------------------------------------------------------------------- /packages/node/src/constants.ts: -------------------------------------------------------------------------------- 1 | import { AddressZero } from "ethers/constants"; 2 | 3 | /** 4 | * We use 0x00...000 to represent an identifier for the ETH token 5 | * in places where values are indexed on token address. Of course, 6 | * in practice, there is no "token address" for ETH because it is a 7 | * native asset on the ethereum blockchain, but using this as an index 8 | * is convenient for storing this data in the same data structure that 9 | * also carries data about ERC20 tokens. 10 | */ 11 | export const CONVENTION_FOR_ETH_TOKEN_ADDRESS = AddressZero; 12 | 13 | // 25446 is 0x6366... or "cf" in ascii, for "Counterfactual". 14 | export const CF_PATH = "m/44'/60'/0'/25446"; 15 | 16 | // Adds indentation, white space, and line break characters to the return-value JSON text to make it easier to read 17 | export const JSON_STRINGIFY_SPACE = 2; 18 | 19 | /** 20 | * @summary This is a `seq` value that messages can take on which 21 | * _should not_ be submitted into the protocol execution. A message 22 | * with seq === -1 should be considered a response to another message 23 | * and this should continue after an IO_SEND_AND_WAIT opcode. 24 | */ 25 | export const UNASSIGNED_SEQ_NO = -1; 26 | -------------------------------------------------------------------------------- /packages/node/src/engine/enums.ts: -------------------------------------------------------------------------------- 1 | enum Opcode { 2 | /** 3 | * Requests a signature on the hash of previously generated EthereumCommitments. 4 | */ 5 | OP_SIGN, 6 | 7 | /** 8 | * Middleware hook to send a ProtocolMessage to a peer. 9 | */ 10 | IO_SEND, 11 | 12 | /** 13 | * Middleware hook to both send and wait for a response from a ProtocolMessage 14 | */ 15 | IO_SEND_AND_WAIT 16 | } 17 | 18 | enum Protocol { 19 | Install = "install", 20 | InstallVirtualApp = "install-virtual-app", 21 | Setup = "setup", 22 | Propose = "propose", 23 | TakeAction = "takeAction", 24 | Uninstall = "uninstall", 25 | UninstallVirtualApp = "uninstall-virtual-app", 26 | Update = "update", 27 | Withdraw = "withdraw" 28 | } 29 | 30 | export { Opcode, Protocol }; 31 | -------------------------------------------------------------------------------- /packages/node/src/engine/middleware.ts: -------------------------------------------------------------------------------- 1 | import { Opcode } from "./enums"; 2 | import { Middleware } from "./types"; 3 | 4 | export class MiddlewareContainer { 5 | public readonly middlewares: { [I in Opcode]: Middleware[] } = { 6 | [Opcode.IO_SEND]: [], 7 | [Opcode.IO_SEND_AND_WAIT]: [], 8 | [Opcode.OP_SIGN]: [] 9 | }; 10 | 11 | public add(scope: Opcode, method: Middleware) { 12 | this.middlewares[scope].push(method); 13 | } 14 | 15 | public async run(opCode: Opcode, args: any[]) { 16 | const middleware = this.middlewares[opCode][0]; 17 | 18 | if (middleware === undefined) { 19 | throw Error( 20 | `Attempted to run middleware for opcode ${opCode} but none existed` 21 | ); 22 | } 23 | 24 | return middleware(args); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /packages/node/src/engine/utils/signature-validator.ts: -------------------------------------------------------------------------------- 1 | import { getAddress, recoverAddress, Signature } from "ethers/utils"; 2 | 3 | import { EthereumCommitment } from "../../ethereum/types"; 4 | 5 | export function assertIsValidSignature( 6 | expectedSigner: string, 7 | commitment?: EthereumCommitment, 8 | signature?: Signature 9 | ) { 10 | if (commitment === undefined) { 11 | throw Error("assertIsValidSignature received an undefined commitment"); 12 | } 13 | 14 | if (signature === undefined) { 15 | throw Error("assertIsValidSignature received an undefined signature"); 16 | } 17 | 18 | const signer = recoverAddress(commitment.hashToSign(), signature); 19 | 20 | if (getAddress(expectedSigner) !== signer) { 21 | throw Error( 22 | `Validating a signature with expected signer ${expectedSigner} but recovered ${signer} for commitment hash ${commitment.hashToSign()}.` 23 | ); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /packages/node/src/ethereum/index.ts: -------------------------------------------------------------------------------- 1 | import { ConditionalTransaction } from "./conditional-transaction-commitment"; 2 | import { SetStateCommitment } from "./set-state-commitment"; 3 | import { SetupCommitment } from "./setup-commitment"; 4 | import { WithdrawERC20Commitment } from "./withdraw-erc20-commitment"; 5 | import { WithdrawETHCommitment } from "./withdraw-eth-commitment"; 6 | 7 | export { 8 | ConditionalTransaction, 9 | SetStateCommitment, 10 | SetupCommitment, 11 | WithdrawETHCommitment, 12 | WithdrawERC20Commitment 13 | }; 14 | -------------------------------------------------------------------------------- /packages/node/src/ethereum/utils/app-identity.ts: -------------------------------------------------------------------------------- 1 | import { AppIdentity } from "@counterfactual/types"; 2 | import { defaultAbiCoder, keccak256 } from "ethers/utils"; 3 | 4 | export function appIdentityToHash(appIdentity: AppIdentity) { 5 | return keccak256( 6 | defaultAbiCoder.encode( 7 | ["uint256", "address[]"], 8 | [appIdentity.channelNonce, appIdentity.participants] 9 | ) 10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /packages/node/src/ethereum/utils/encodings.ts: -------------------------------------------------------------------------------- 1 | // NOTE: It is important that the strings end with a comma and not a semicolon, 2 | // these are not struct declarations but simply multi-line tuple encodings. 3 | export const APP_IDENTITY = ` 4 | tuple( 5 | address[] participants, 6 | address appDefinition, 7 | uint256 defaultTimeout 8 | )`; 9 | -------------------------------------------------------------------------------- /packages/node/src/ethereum/utils/index.ts: -------------------------------------------------------------------------------- 1 | import { EthereumCommitment } from "../types"; 2 | 3 | export { EthereumCommitment }; 4 | -------------------------------------------------------------------------------- /packages/node/src/ethereum/withdraw-erc20-commitment.ts: -------------------------------------------------------------------------------- 1 | import ERC20 from "@counterfactual/cf-funding-protocol-contracts/expected-build-artifacts/ERC20.json"; 2 | import { BigNumberish, Interface } from "ethers/utils"; 3 | 4 | import { MultisigCommitment } from "./multisig-commitment"; 5 | import { MultisigOperation, MultisigTransaction } from "./types"; 6 | 7 | export class WithdrawERC20Commitment extends MultisigCommitment { 8 | public constructor( 9 | public readonly multisigAddress: string, 10 | public readonly multisigOwners: string[], 11 | public readonly to: string, 12 | public readonly value: BigNumberish, 13 | public readonly tokenAddress: string 14 | ) { 15 | super(multisigAddress, multisigOwners); 16 | } 17 | 18 | public getTransactionDetails(): MultisigTransaction { 19 | return { 20 | to: this.tokenAddress, 21 | value: 0, 22 | data: new Interface(ERC20.abi).functions.transfer.encode([ 23 | this.to, 24 | this.value 25 | ]), 26 | operation: MultisigOperation.Call 27 | }; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /packages/node/src/ethereum/withdraw-eth-commitment.ts: -------------------------------------------------------------------------------- 1 | import { bigNumberify, BigNumberish } from "ethers/utils"; 2 | 3 | import { MultisigCommitment } from "./multisig-commitment"; 4 | import { MultisigOperation, MultisigTransaction } from "./types"; 5 | 6 | export class WithdrawETHCommitment extends MultisigCommitment { 7 | public constructor( 8 | public readonly multisigAddress: string, 9 | public readonly multisigOwners: string[], 10 | public readonly to: string, 11 | public readonly value: BigNumberish 12 | ) { 13 | super(multisigAddress, multisigOwners); 14 | } 15 | 16 | public getTransactionDetails(): MultisigTransaction { 17 | return { 18 | to: this.to, 19 | value: bigNumberify(this.value), 20 | data: "0x", 21 | operation: MultisigOperation.Call 22 | }; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /packages/node/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./node"; 2 | export * from "./private-keys-generator"; 3 | export * from "./types"; 4 | export * from "./methods/errors"; 5 | export * from "rpc-server"; 6 | export { getNetworkEnum, EthereumNetworkName } from "./network-configuration"; 7 | -------------------------------------------------------------------------------- /packages/node/src/message-handling/handle-node-message.ts: -------------------------------------------------------------------------------- 1 | import { RequestHandler } from "../request-handler"; 2 | import { RejectProposalMessage } from "../types"; 3 | 4 | export async function handleRejectProposalMessage( 5 | requestHandler: RequestHandler, 6 | receivedRejectProposalMessage: RejectProposalMessage 7 | ) { 8 | const { store } = requestHandler; 9 | const { 10 | data: { appInstanceId } 11 | } = receivedRejectProposalMessage; 12 | 13 | const stateChannel = await store.getChannelFromAppInstanceID(appInstanceId); 14 | 15 | await store.saveStateChannel(stateChannel.removeProposal(appInstanceId)); 16 | } 17 | -------------------------------------------------------------------------------- /packages/node/src/methods/app-instance/get-all/controller.ts: -------------------------------------------------------------------------------- 1 | import { AppInstanceJson, Node } from "@counterfactual/types"; 2 | import { jsonRpcMethod } from "rpc-server"; 3 | 4 | import { StateChannel } from "../../../models"; 5 | import { RequestHandler } from "../../../request-handler"; 6 | import { NodeController } from "../../controller"; 7 | 8 | /** 9 | * Gets all installed appInstances across all of the channels open on 10 | * this Node. 11 | */ 12 | export default class GetAppInstancesController extends NodeController { 13 | @jsonRpcMethod(Node.RpcMethodName.GET_APP_INSTANCES) 14 | public executeMethod = super.executeMethod; 15 | 16 | protected async executeMethodImplementation( 17 | requestHandler: RequestHandler 18 | ): Promise { 19 | const { store } = requestHandler; 20 | 21 | const channels = await store.getStateChannelsMap(); 22 | 23 | const appInstances = Array.from(channels.values()).reduce( 24 | (acc: AppInstanceJson[], channel: StateChannel) => { 25 | acc.push( 26 | ...Array.from(channel.appInstances.values()).map(appInstance => 27 | appInstance.toJson() 28 | ) 29 | ); 30 | return acc; 31 | }, 32 | [] 33 | ); 34 | 35 | return { 36 | appInstances 37 | }; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /packages/node/src/methods/app-instance/get-app-instance/controller.ts: -------------------------------------------------------------------------------- 1 | import { Node } from "@counterfactual/types"; 2 | import { jsonRpcMethod } from "rpc-server"; 3 | 4 | import { RequestHandler } from "../../../request-handler"; 5 | import { NodeController } from "../../controller"; 6 | import { NO_APP_INSTANCE_ID_TO_GET_DETAILS } from "../../errors"; 7 | 8 | /** 9 | * Handles the retrieval of an AppInstance. 10 | * @param this 11 | * @param params 12 | */ 13 | export default class GetAppInstanceDetailsController extends NodeController { 14 | @jsonRpcMethod(Node.RpcMethodName.GET_APP_INSTANCE_DETAILS) 15 | protected async executeMethodImplementation( 16 | requestHandler: RequestHandler, 17 | params: Node.GetAppInstanceDetailsParams 18 | ): Promise { 19 | const { store } = requestHandler; 20 | const { appInstanceId } = params; 21 | 22 | if (!appInstanceId) { 23 | throw Error(NO_APP_INSTANCE_ID_TO_GET_DETAILS); 24 | } 25 | 26 | return { 27 | appInstance: (await store.getAppInstance(appInstanceId)).toJson() 28 | }; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /packages/node/src/methods/app-instance/get-state/controller.ts: -------------------------------------------------------------------------------- 1 | import { Node } from "@counterfactual/types"; 2 | import { jsonRpcMethod } from "rpc-server"; 3 | 4 | import { RequestHandler } from "../../../request-handler"; 5 | import { NodeController } from "../../controller"; 6 | import { NO_APP_INSTANCE_ID_FOR_GET_STATE } from "../../errors"; 7 | 8 | /** 9 | * Handles the retrieval of an AppInstance's state. 10 | * @param this 11 | * @param params 12 | */ 13 | export default class GetStateController extends NodeController { 14 | @jsonRpcMethod(Node.RpcMethodName.GET_STATE) 15 | protected async executeMethodImplementation( 16 | requestHandler: RequestHandler, 17 | params: Node.GetStateParams 18 | ): Promise { 19 | const { store } = requestHandler; 20 | const { appInstanceId } = params; 21 | 22 | if (!appInstanceId) { 23 | throw Error(NO_APP_INSTANCE_ID_FOR_GET_STATE); 24 | } 25 | 26 | const appInstance = await store.getAppInstance(appInstanceId); 27 | 28 | return { 29 | state: appInstance.state 30 | }; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /packages/node/src/methods/app-instance/get-token-indexed-free-balances/controller.ts: -------------------------------------------------------------------------------- 1 | import { Node } from "@counterfactual/types"; 2 | import { jsonRpcMethod } from "rpc-server"; 3 | 4 | import { RequestHandler } from "../../../request-handler"; 5 | import { NodeController } from "../../controller"; 6 | 7 | export default class GetTokenIndexedFreeBalancesController extends NodeController { 8 | @jsonRpcMethod(Node.RpcMethodName.GET_TOKEN_INDEXED_FREE_BALANCE_STATES) 9 | public executeMethod = super.executeMethod; 10 | 11 | protected async executeMethodImplementation( 12 | requestHandler: RequestHandler, 13 | params: Node.GetTokenIndexedFreeBalanceStatesParams 14 | ): Promise { 15 | const { store } = requestHandler; 16 | const { multisigAddress } = params; 17 | 18 | if (!multisigAddress) { 19 | throw Error( 20 | "getTokenIndexedFreeBalanceStates method was given undefined multisigAddress" 21 | ); 22 | } 23 | 24 | const stateChannel = await store.getStateChannel(multisigAddress); 25 | 26 | return stateChannel.getFreeBalanceClass().toTokenIndexedCoinTransferMap(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /packages/node/src/methods/app-instance/reject-install-virtual/controller.ts: -------------------------------------------------------------------------------- 1 | import { Node } from "@counterfactual/types"; 2 | 3 | import { RequestHandler } from "../../../request-handler"; 4 | import { NODE_EVENTS, RejectInstallVirtualMessage } from "../../../types"; 5 | 6 | export default async function rejectInstallVirtualController( 7 | requestHandler: RequestHandler, 8 | params: Node.RejectInstallParams 9 | ): Promise { 10 | const { store, messagingService, publicIdentifier } = requestHandler; 11 | 12 | const { appInstanceId } = params; 13 | 14 | const proposal = await store.getAppInstanceProposal(appInstanceId); 15 | 16 | const stateChannel = await store.getChannelFromAppInstanceID(appInstanceId); 17 | 18 | await store.saveStateChannel(stateChannel.removeProposal(appInstanceId)); 19 | 20 | const rejectInstallVirtualMsg: RejectInstallVirtualMessage = { 21 | from: publicIdentifier, 22 | type: NODE_EVENTS.REJECT_INSTALL_VIRTUAL, 23 | data: { 24 | appInstanceId 25 | } 26 | }; 27 | 28 | await messagingService.send( 29 | proposal.proposedByIdentifier, 30 | rejectInstallVirtualMsg 31 | ); 32 | 33 | return {}; 34 | } 35 | -------------------------------------------------------------------------------- /packages/node/src/methods/app-instance/uninstall-virtual/operation.ts: -------------------------------------------------------------------------------- 1 | import { Provider } from "ethers/providers"; 2 | 3 | import { Protocol, ProtocolRunner } from "../../../engine"; 4 | import { Store } from "../../../store"; 5 | 6 | export async function uninstallVirtualAppInstanceFromChannel( 7 | store: Store, 8 | protocolRunner: ProtocolRunner, 9 | provider: Provider, 10 | initiatorXpub: string, 11 | responderXpub: string, 12 | intermediaryXpub: string, 13 | appInstanceId: string 14 | ): Promise { 15 | const stateChannel = await store.getChannelFromAppInstanceID(appInstanceId); 16 | 17 | const appInstance = stateChannel.getAppInstance(appInstanceId); 18 | 19 | await protocolRunner.initiateProtocol(Protocol.UninstallVirtualApp, { 20 | initiatorXpub, 21 | responderXpub, 22 | intermediaryXpub, 23 | targetOutcome: await appInstance.computeOutcome( 24 | appInstance.state, 25 | provider 26 | ), 27 | targetAppIdentityHash: appInstance.identityHash 28 | }); 29 | } 30 | -------------------------------------------------------------------------------- /packages/node/src/methods/app-instance/uninstall/operation.ts: -------------------------------------------------------------------------------- 1 | import { Protocol, ProtocolRunner } from "../../../engine"; 2 | import { Store } from "../../../store"; 3 | 4 | export async function uninstallAppInstanceFromChannel( 5 | store: Store, 6 | protocolRunner: ProtocolRunner, 7 | initiatorXpub: string, 8 | responderXpub: string, 9 | appInstanceId: string 10 | ): Promise { 11 | const stateChannel = await store.getChannelFromAppInstanceID(appInstanceId); 12 | 13 | const appInstance = stateChannel.getAppInstance(appInstanceId); 14 | 15 | await protocolRunner.initiateProtocol(Protocol.Uninstall, { 16 | initiatorXpub, 17 | responderXpub, 18 | multisigAddress: stateChannel.multisigAddress, 19 | appIdentityHash: appInstance.identityHash 20 | }); 21 | } 22 | -------------------------------------------------------------------------------- /packages/node/src/methods/proposed-app-instance/get-all/controller.ts: -------------------------------------------------------------------------------- 1 | import { Node } from "@counterfactual/types"; 2 | import { jsonRpcMethod } from "rpc-server"; 3 | 4 | import { RequestHandler } from "../../../request-handler"; 5 | import { NodeController } from "../../controller"; 6 | 7 | export default class GetProposedAppInstancesController extends NodeController { 8 | @jsonRpcMethod(Node.RpcMethodName.GET_PROPOSED_APP_INSTANCES) 9 | protected async executeMethodImplementation( 10 | requestHandler: RequestHandler 11 | ): Promise { 12 | return { 13 | appInstances: await requestHandler.store.getProposedAppInstances() 14 | }; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/node/src/methods/proposed-app-instance/get/controller.ts: -------------------------------------------------------------------------------- 1 | import { Node } from "@counterfactual/types"; 2 | 3 | import { RequestHandler } from "../../../request-handler"; 4 | import { NodeController } from "../../controller"; 5 | 6 | export default class GetProposedAppInstanceController extends NodeController { 7 | public static readonly methodName = Node.MethodName.GET_PROPOSED_APP_INSTANCE; 8 | 9 | protected async executeMethodImplementation( 10 | requestHandler: RequestHandler, 11 | params: Node.GetProposedAppInstanceParams 12 | ): Promise { 13 | return { 14 | appInstance: await requestHandler.store.getAppInstanceProposal( 15 | params.appInstanceId 16 | ) 17 | }; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /packages/node/src/methods/state-channel/get-all/controller.ts: -------------------------------------------------------------------------------- 1 | import { Node } from "@counterfactual/types"; 2 | import { jsonRpcMethod } from "rpc-server"; 3 | 4 | import { StateChannelJSON } from "../../../models"; 5 | import { RequestHandler } from "../../../request-handler"; 6 | import { NodeController } from "../../controller"; 7 | 8 | export default class GetStateChannelController extends NodeController { 9 | @jsonRpcMethod(Node.RpcMethodName.GET_STATE_CHANNEL) 10 | public executeMethod = super.executeMethod; 11 | 12 | protected async executeMethodImplementation( 13 | requestHandler: RequestHandler, 14 | params: { multisigAddress: string } 15 | ): Promise<{ data: StateChannelJSON }> { 16 | return { 17 | data: (await requestHandler.store.getStateChannel( 18 | params.multisigAddress 19 | )).toJson() 20 | }; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /packages/node/src/methods/state-channel/get/controller.ts: -------------------------------------------------------------------------------- 1 | import { Node } from "@counterfactual/types"; 2 | import { jsonRpcMethod } from "rpc-server"; 3 | 4 | import { RequestHandler } from "../../../request-handler"; 5 | import { NodeController } from "../../controller"; 6 | 7 | export default class GetAllChannelAddressesController extends NodeController { 8 | @jsonRpcMethod(Node.RpcMethodName.GET_CHANNEL_ADDRESSES) 9 | public executeMethod = super.executeMethod; 10 | 11 | protected async executeMethodImplementation( 12 | requestHandler: RequestHandler 13 | ): Promise { 14 | return { 15 | multisigAddresses: [ 16 | ...(await requestHandler.store.getStateChannelsMap()).keys() 17 | ] 18 | }; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /packages/node/src/methods/state-channel/withdraw/operation.ts: -------------------------------------------------------------------------------- 1 | import { Node } from "@counterfactual/types"; 2 | 3 | import { CONVENTION_FOR_ETH_TOKEN_ADDRESS } from "../../../constants"; 4 | import { Protocol } from "../../../engine"; 5 | import { StateChannel } from "../../../models"; 6 | import { RequestHandler } from "../../../request-handler"; 7 | 8 | export async function runWithdrawProtocol( 9 | requestHandler: RequestHandler, 10 | params: Node.WithdrawParams 11 | ) { 12 | const { publicIdentifier, protocolRunner, store } = requestHandler; 13 | const { multisigAddress, amount } = params; 14 | 15 | const tokenAddress = params.tokenAddress || CONVENTION_FOR_ETH_TOKEN_ADDRESS; 16 | 17 | const [peerAddress] = await StateChannel.getPeersAddressFromChannel( 18 | publicIdentifier, 19 | store, 20 | multisigAddress 21 | ); 22 | 23 | await protocolRunner.initiateProtocol(Protocol.Withdraw, { 24 | amount, 25 | tokenAddress, 26 | multisigAddress, 27 | recipient: params.recipient as string, 28 | initiatorXpub: publicIdentifier, 29 | responderXpub: peerAddress 30 | }); 31 | } 32 | -------------------------------------------------------------------------------- /packages/node/src/models/app-instance-proposal.ts: -------------------------------------------------------------------------------- 1 | import { 2 | AppABIEncodings, 3 | OutcomeType, 4 | SolidityValueType 5 | } from "@counterfactual/types"; 6 | 7 | export interface AppInstanceProposal { 8 | identityHash: string; 9 | appDefinition: string; 10 | abiEncodings: AppABIEncodings; 11 | initiatorDeposit: string; 12 | initiatorDepositTokenAddress: string; 13 | responderDeposit: string; 14 | responderDepositTokenAddress: string; 15 | timeout: string; 16 | initialState: SolidityValueType; 17 | appSeqNo: number; 18 | proposedByIdentifier: string; 19 | proposedToIdentifier: string; 20 | intermediaryIdentifier?: string; 21 | outcomeType: OutcomeType; 22 | } 23 | -------------------------------------------------------------------------------- /packages/node/src/models/index.ts: -------------------------------------------------------------------------------- 1 | import { AppInstance } from "./app-instance"; 2 | import { AppInstanceProposal } from "./app-instance-proposal"; 3 | import { StateChannel, StateChannelJSON } from "./state-channel"; 4 | 5 | export { AppInstance, AppInstanceProposal, StateChannel, StateChannelJSON }; 6 | -------------------------------------------------------------------------------- /packages/node/src/utils/auto-nonce-wallet.ts: -------------------------------------------------------------------------------- 1 | import { Wallet } from "ethers"; 2 | import { TransactionRequest, TransactionResponse } from "ethers/providers"; 3 | 4 | export default class AutoNonceWallet extends Wallet { 5 | private noncePromise; 6 | 7 | async sendTransaction(tx: TransactionRequest): Promise { 8 | if (!tx.nonce) { 9 | if (this.noncePromise === undefined) { 10 | this.noncePromise = this.provider.getTransactionCount(this.address); 11 | } 12 | 13 | const tmp = this.noncePromise; 14 | 15 | this.noncePromise = this.noncePromise.then(nonce => nonce + 1); 16 | 17 | tx.nonce = await tmp; 18 | } 19 | 20 | return await super.sendTransaction(tx); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /packages/node/src/utils/deferred.ts: -------------------------------------------------------------------------------- 1 | // Wraps an internal promise and provide public methods to resolve/reject the 2 | // internal promise. The internal promise is also publicly available so it can 3 | // be await-ed on. 4 | export class Deferred { 5 | private readonly internalPromise: Promise; 6 | private internalResolve!: (value?: T | PromiseLike) => void; 7 | private internalReject!: (reason?: any) => void; 8 | 9 | constructor() { 10 | this.internalPromise = new Promise((resolve, reject) => { 11 | this.internalResolve = resolve; 12 | this.internalReject = reject; 13 | }); 14 | } 15 | 16 | get promise(): Promise { 17 | return this.internalPromise; 18 | } 19 | 20 | resolve = (value?: T | PromiseLike): void => { 21 | this.internalResolve(value); 22 | }; 23 | 24 | reject = (reason?: any): void => { 25 | this.internalReject(reason); 26 | }; 27 | } 28 | -------------------------------------------------------------------------------- /packages/node/test/engine/integration/bignumber-jest-matcher.ts: -------------------------------------------------------------------------------- 1 | import { BigNumber, BigNumberish } from "ethers/utils"; 2 | 3 | declare global { 4 | namespace jest { 5 | interface Matchers { 6 | toBeEq(expected: BigNumberish): BigNumber; 7 | toBeLt(expected: BigNumberish): BigNumber; 8 | } 9 | } 10 | } 11 | 12 | export function toBeEq(received: BigNumber, argument: BigNumberish) { 13 | const pass = received.eq(argument); 14 | return { 15 | pass, 16 | message: () => 17 | `expected ${received} to ${pass ? "not " : ""}be equal to ${argument}` 18 | }; 19 | } 20 | 21 | export function toBeLt(received: BigNumber, argument: BigNumberish) { 22 | const pass = received.lt(argument); 23 | return { 24 | pass, 25 | message: () => 26 | `expected ${received} to ${pass ? "not " : ""}be less than ${argument}` 27 | }; 28 | } 29 | -------------------------------------------------------------------------------- /packages/node/test/engine/integration/connect-ganache.ts: -------------------------------------------------------------------------------- 1 | import { Wallet } from "ethers"; 2 | import { JsonRpcProvider } from "ethers/providers"; 3 | 4 | import { generateNewFundedWallet } from "../../integration/setup"; 5 | 6 | export async function connectToGanache(): Promise< 7 | [JsonRpcProvider, Wallet, number] 8 | > { 9 | const provider = new JsonRpcProvider(global["ganacheURL"]); 10 | const wallet = await generateNewFundedWallet( 11 | global["fundedPrivateKey"], 12 | provider 13 | ); 14 | const networkId = (await provider.getNetwork()).chainId; 15 | return [provider, wallet, networkId]; 16 | } 17 | -------------------------------------------------------------------------------- /packages/node/test/engine/integration/waffle-type.ts: -------------------------------------------------------------------------------- 1 | export type WaffleLegacyOutput = { 2 | contractName?: string; 3 | networks?: { 4 | [x: string]: { 5 | address: string; 6 | events: {}; 7 | links: { [x: string]: string | undefined }; 8 | transactionHash: string; 9 | }; 10 | }; 11 | }; 12 | -------------------------------------------------------------------------------- /packages/node/test/engine/mocks.ts: -------------------------------------------------------------------------------- 1 | import { 2 | EXPECTED_CONTRACT_NAMES_IN_NETWORK_CONTEXT, 3 | NetworkContext 4 | } from "@counterfactual/types"; 5 | import { getAddress, hexlify, randomBytes } from "ethers/utils"; 6 | 7 | /// todo(xuanji): make this random but deterministically generated from some seed 8 | export function generateRandomNetworkContext(): NetworkContext { 9 | return EXPECTED_CONTRACT_NAMES_IN_NETWORK_CONTEXT.reduce( 10 | (acc, contractName) => ({ 11 | ...acc, 12 | [contractName]: getAddress(hexlify(randomBytes(20))) 13 | }), 14 | {} as NetworkContext 15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /packages/node/test/engine/unit/models/state-channel/state-channel.spec.ts: -------------------------------------------------------------------------------- 1 | import { getAddress, hexlify, randomBytes } from "ethers/utils"; 2 | 3 | import { StateChannel } from "../../../../../src/models"; 4 | import { getRandomExtendedPubKeys } from "../../../integration/random-signing-keys"; 5 | 6 | describe("StateChannel", () => { 7 | it("should be able to instantiate", () => { 8 | const multisigAddress = getAddress(hexlify(randomBytes(20))); 9 | const xpubs = getRandomExtendedPubKeys(2); 10 | 11 | const sc = new StateChannel(multisigAddress, xpubs); 12 | 13 | expect(sc).not.toBe(null); 14 | expect(sc).not.toBe(undefined); 15 | expect(sc.multisigAddress).toBe(multisigAddress); 16 | expect(sc.userNeuteredExtendedKeys).toBe(xpubs); 17 | expect(sc.numActiveApps).toBe(0); 18 | expect(sc.numProposedApps).toBe(0); 19 | }); 20 | }); 21 | -------------------------------------------------------------------------------- /packages/node/test/global-setup.jest.ts: -------------------------------------------------------------------------------- 1 | import { LocalGanacheServer } from "@counterfactual/local-ganache-server"; 2 | import dotenvExtended from "dotenv-extended"; 3 | 4 | import { 5 | A_EXTENDED_PRIVATE_KEY, 6 | B_EXTENDED_PRIVATE_KEY, 7 | C_EXTENDED_PRIVATE_KEY 8 | } from "./test-constants.jest"; 9 | 10 | dotenvExtended.load(); 11 | 12 | export default async function globalSetup() { 13 | const chain = new LocalGanacheServer([ 14 | A_EXTENDED_PRIVATE_KEY, 15 | B_EXTENDED_PRIVATE_KEY, 16 | C_EXTENDED_PRIVATE_KEY 17 | ]); 18 | await chain.runMigrations(); 19 | global["chain"] = chain; 20 | } 21 | -------------------------------------------------------------------------------- /packages/node/test/global-teardown.jest.ts: -------------------------------------------------------------------------------- 1 | export default async function globalTeardown() { 2 | global["chain"].server.close(); 3 | } 4 | -------------------------------------------------------------------------------- /packages/node/test/integration/get-state-deposit-holder-address.spec.ts: -------------------------------------------------------------------------------- 1 | import { Node as NodeTypes } from "@counterfactual/types"; 2 | 3 | import { jsonRpcDeserialize, Node } from "../../src"; 4 | 5 | import { setup, SetupContext } from "./setup"; 6 | 7 | describe("Node method follows spec - getStateDepositHolderAddress", () => { 8 | let nodeA: Node; 9 | let nodeB: Node; 10 | 11 | beforeAll(async () => { 12 | const context: SetupContext = await setup(global); 13 | nodeA = context["A"].node; 14 | nodeB = context["B"].node; 15 | }); 16 | 17 | it("can accept a valid call and return correctly formatted address", async () => { 18 | const owners = [nodeA.publicIdentifier, nodeB.publicIdentifier]; 19 | 20 | const { 21 | result: { 22 | result: { address } 23 | } 24 | } = await nodeA.rpcRouter.dispatch( 25 | jsonRpcDeserialize({ 26 | id: Date.now(), 27 | method: NodeTypes.RpcMethodName.GET_STATE_DEPOSIT_HOLDER_ADDRESS, 28 | params: { owners }, 29 | jsonrpc: "2.0" 30 | }) 31 | ); 32 | 33 | expect(address.length).toBe(42); 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /packages/node/test/integration/simple-transfer.ts: -------------------------------------------------------------------------------- 1 | import { AppABIEncodings } from "@counterfactual/types"; 2 | import { Zero } from "ethers/constants"; 3 | import { bigNumberify, BigNumberish } from "ethers/utils"; 4 | 5 | const singleAssetTwoPartyCoinTransferEncoding = ` 6 | tuple(address to, uint256 amount)[2] 7 | `; 8 | 9 | export const simpleTransferAbiEncodings: AppABIEncodings = { 10 | stateEncoding: ` 11 | tuple( 12 | ${singleAssetTwoPartyCoinTransferEncoding} coinTransfers 13 | )`, 14 | actionEncoding: "" 15 | }; 16 | 17 | export function initialSimpleTransferState( 18 | senderAddr: string, 19 | receiverAddr: string, 20 | amount: BigNumberish = 1 21 | ) { 22 | return { 23 | coinTransfers: [ 24 | { 25 | amount: bigNumberify(amount), 26 | to: senderAddr 27 | }, 28 | { 29 | to: receiverAddr, 30 | amount: Zero 31 | } 32 | ] 33 | }; 34 | } 35 | -------------------------------------------------------------------------------- /packages/node/test/integration/tic-tac-toe.ts: -------------------------------------------------------------------------------- 1 | import { AppABIEncodings } from "@counterfactual/types"; 2 | 3 | export const tttAbiEncodings: AppABIEncodings = { 4 | stateEncoding: ` 5 | tuple( 6 | uint256 versionNumber, 7 | uint256 winner, 8 | uint256[3][3] board 9 | )`, 10 | actionEncoding: ` 11 | tuple( 12 | uint8 actionType, 13 | uint256 playX, 14 | uint256 playY, 15 | tuple( 16 | uint8 winClaimType, 17 | uint256 idx 18 | ) winClaim 19 | )" 20 | ` 21 | }; 22 | 23 | export const validAction = { 24 | actionType: 0, 25 | playX: 0, 26 | playY: 0, 27 | winClaim: { 28 | winClaimType: 0, 29 | idx: 0 30 | } 31 | }; 32 | 33 | export function initialEmptyTTTState() { 34 | return { 35 | versionNumber: 0, 36 | winner: 0, 37 | board: [[0, 0, 0], [0, 0, 0], [0, 0, 0]] 38 | }; 39 | } 40 | -------------------------------------------------------------------------------- /packages/node/test/node-test-environment.jest.js: -------------------------------------------------------------------------------- 1 | const NodeJSEnvironment = require("jest-environment-node"); 2 | 3 | require("dotenv-extended").load(); 4 | 5 | // This environment runs for _every test suite_. 6 | 7 | class NodeEnvironment extends NodeJSEnvironment { 8 | constructor(config) { 9 | super(config); 10 | } 11 | 12 | async setup() { 13 | await super.setup(); 14 | 15 | const chain = global["chain"]; 16 | 17 | this.global.networkContext = chain.networkContext; 18 | this.global.fundedPrivateKey = chain.fundedPrivateKey; 19 | this.global.ganacheURL = `http://localhost:${process.env.GANACHE_PORT}`; 20 | } 21 | 22 | 23 | async teardown() { 24 | await super.teardown(); 25 | } 26 | 27 | runScript(script) { 28 | return super.runScript(script); 29 | } 30 | } 31 | 32 | module.exports = NodeEnvironment; 33 | -------------------------------------------------------------------------------- /packages/node/test/services/memory-lock-service.ts: -------------------------------------------------------------------------------- 1 | import { Node } from "@counterfactual/types"; 2 | 3 | import { Lock } from "./lock"; 4 | 5 | export class MemoryLockService implements Node.ILockService { 6 | public readonly locks: Map = new Map(); 7 | 8 | async acquireLock( 9 | lockName: string, 10 | callback: (...args: any[]) => any, 11 | timeout: number 12 | ): Promise { 13 | const lock = this.getOrCreateLock(lockName); 14 | 15 | let retval = null; 16 | let rejectReason = null; 17 | let unlockKey = ""; 18 | 19 | try { 20 | unlockKey = await lock.acquireLock(timeout); 21 | retval = await callback(); 22 | } catch (e) { 23 | // TODO: check exception... if the lock failed 24 | rejectReason = e; 25 | } finally { 26 | await lock.releaseLock(unlockKey); 27 | } 28 | 29 | if (rejectReason) throw rejectReason; 30 | 31 | return retval; 32 | } 33 | 34 | private getOrCreateLock(lockName: string) { 35 | if (!this.locks.has(lockName)) { 36 | this.locks.set(lockName, new Lock(lockName)); 37 | } 38 | return this.locks.get(lockName)!; 39 | } 40 | } 41 | 42 | export default MemoryLockService; 43 | -------------------------------------------------------------------------------- /packages/node/test/services/memory-messaging-service.ts: -------------------------------------------------------------------------------- 1 | import { Node } from "@counterfactual/types"; 2 | import { EventEmitter } from "events"; 3 | 4 | export class MemoryMessagingService implements Node.IMessagingService { 5 | private readonly eventEmitter: EventEmitter = new EventEmitter(); 6 | constructor() {} 7 | 8 | async send(to: string, msg: Node.NodeMessage): Promise { 9 | this.eventEmitter.emit(to, msg); 10 | } 11 | 12 | onReceive(address: string, callback: (msg: Node.NodeMessage) => void) { 13 | this.eventEmitter.on(address, msg => { 14 | callback(msg); 15 | }); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /packages/node/test/services/mock-messaging-service.ts: -------------------------------------------------------------------------------- 1 | import { Node } from "@counterfactual/types"; 2 | 3 | class MockMessagingService implements Node.IMessagingService { 4 | async send() {} 5 | onReceive() {} 6 | } 7 | 8 | export default new MockMessagingService(); 9 | -------------------------------------------------------------------------------- /packages/node/test/services/mock-store-service.ts: -------------------------------------------------------------------------------- 1 | import { Node } from "@counterfactual/types"; 2 | 3 | class MockStoreService implements Node.IStoreService { 4 | get() { 5 | return Promise.resolve(true); 6 | } 7 | 8 | set() { 9 | return Promise.resolve(); 10 | } 11 | } 12 | 13 | const mockStoreService = new MockStoreService(); 14 | export default mockStoreService; 15 | -------------------------------------------------------------------------------- /packages/node/test/test-constants.jest.ts: -------------------------------------------------------------------------------- 1 | export const A_EXTENDED_PRIVATE_KEY = 2 | "xprv9s21ZrQH143K3SVqyEdbm23B4ZEfFo5f558HcDPgJAi1PFCvzmUXYgzNVMgsV6KqBdDa62MfzemsXzjUTvimZXDTBo838DKoLECLYMaR5oQ"; 3 | 4 | export const B_EXTENDED_PRIVATE_KEY = 5 | "xprv9s21ZrQH143K4HKEWFBnzxZDUCdpozADLYPEW8aSJsvKdSV1Q77xZ1veGCVRPFs3Q2d6vuZAzBU4PH6u5DagiWtGV3TyB72uxfpEZDiwfPR"; 6 | 7 | export const C_EXTENDED_PRIVATE_KEY = 8 | "xprv9s21ZrQH143K4F9XCPTFbiZxzaRqWzC2dTv3sxe24gZcTLQ3sUtJHYaa42r1JSQ1p8dsfNNm5W7bj4z3A5zgqLZN32fXxjNuDdTQmvMbCVs"; 9 | -------------------------------------------------------------------------------- /packages/node/test/unit/node.spec.ts: -------------------------------------------------------------------------------- 1 | import { JsonRpcProvider } from "ethers/providers"; 2 | 3 | import { Node } from "../../src/node"; 4 | import { MemoryStoreService } from "../services/memory-store-service"; 5 | import mockMessagingService from "../services/mock-messaging-service"; 6 | 7 | describe("Node", () => { 8 | it("is defined", () => { 9 | expect(Node).toBeDefined(); 10 | }); 11 | 12 | it("can be created", async () => { 13 | const node = await Node.create( 14 | mockMessagingService, 15 | new MemoryStoreService(), 16 | global["networkContext"], 17 | { STORE_KEY_PREFIX: "./node.spec.ts-test-file" }, 18 | new JsonRpcProvider(global["ganacheURL"]) 19 | ); 20 | 21 | expect(node).toBeDefined(); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /packages/node/truffle-config.js: -------------------------------------------------------------------------------- 1 | const HDWalletProvider = require("truffle-hdwallet-provider"); 2 | 3 | require("dotenv-safe").config(); 4 | 5 | module.exports = { 6 | contracts_build_directory: "./build", 7 | contracts_directory: "../contracts", 8 | migrations_directory: "../contracts/migrations", 9 | networks: { 10 | machine: { 11 | network_id: process.env.DEV_GANACHE_NETWORK_ID, 12 | provider: function() { 13 | return new HDWalletProvider( 14 | process.env.DEV_GANACHE_MNEMONIC, 15 | `http://${process.env.DEV_GANACHE_HOST}:${process.env.DEV_GANACHE_PORT}/` 16 | ); 17 | } 18 | } 19 | }, 20 | compilers: { 21 | solc: { 22 | version: "../../node_modules/solc", 23 | settings: { 24 | evmVersion: "constantinople" 25 | } 26 | } 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /packages/node/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "declaration": true, 5 | "declarationDir": "dist/", 6 | "outDir": "dist/", 7 | "resolveJsonModule": true, 8 | "typeRoots": [ 9 | "../../node_modules/@types", 10 | "../../node_modules/@counterfactual/typescript-typings/types" 11 | ], 12 | "types": ["jest"] 13 | }, 14 | 15 | "references": [ 16 | { "path": "../types" }, 17 | { "path": "../local-ganache-server"} 18 | ], 19 | "include": ["src", "test"] 20 | } 21 | -------------------------------------------------------------------------------- /packages/node/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../../tslint.json"], 3 | "rules": { 4 | // Needed because `ProtocolExecutionFlow` uses numbered object keys 5 | "object-literal-shorthand": false 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /packages/simple-hub-server/.env-cmdrc: -------------------------------------------------------------------------------- 1 | { 2 | "development": { 3 | "STORE_PREFIX": "development", 4 | "DB_ENGINE": "pg", 5 | "DB_CONNECTION_STRING": "postgresql://postgres:postgres@localhost:5432/postgres", 6 | "PORT": "9000", 7 | "NODE_PRIVATE_KEY": "0x0123456789012345678901234567890123456789012345678901234567890123", 8 | "NODE_EXTENDED_PRIVATE_KEY": "xprv9s21ZrQH143K3SUeRQphNwVqZc2hJg3bZiNQwbMyTGcyDuLXay2xWCMDmC2Tu8JHiCGbyy3rYjVq4LRYZ716Yn1WSkMUx2VzpgJr79g3iEt", 9 | "FIREBASE_SERVER_HOST": "localhost", 10 | "FIREBASE_SERVER_PORT": "5555", 11 | "ETHEREUM_NETWORK": "kovan", 12 | "PLAYGROUND_SCHEMA_VERSION": 2, 13 | "PLAYGROUND_MAINTENANCE_MODE": "false", 14 | "NODE_ENV": "development", 15 | "LOG_LEVEL": "DEBUG" 16 | }, 17 | "test": { 18 | "STORE_PREFIX": "test", 19 | "DB_ENGINE": "sqlite3", 20 | "DB_FILE": "./test/test-db.sqlite", 21 | "PORT": "9001", 22 | "NODE_PRIVATE_KEY": "0x0123456789012345678901234567890123456789012345678901234567890123", 23 | "GANACHE_PORT": "8545", 24 | "FIREBASE_SERVER_HOST": "localhost", 25 | "FIREBASE_SERVER_PORT": "5555", 26 | "NODE_ENV": "test" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /packages/simple-hub-server/.env.schema: -------------------------------------------------------------------------------- 1 | NODE_EXTENDED_PRIVATE_KEY= 2 | -------------------------------------------------------------------------------- /packages/simple-hub-server/.gitignore: -------------------------------------------------------------------------------- 1 | **/test-db.sqlite 2 | .env 3 | -------------------------------------------------------------------------------- /packages/simple-hub-server/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM postgres:11.5-alpine 2 | COPY database/schema/playground_db.sql /docker-entrypoint-initdb.d/ 3 | -------------------------------------------------------------------------------- /packages/simple-hub-server/Procfile: -------------------------------------------------------------------------------- 1 | web: npm run serve 2 | -------------------------------------------------------------------------------- /packages/simple-hub-server/database/schema/playground_db.sql: -------------------------------------------------------------------------------- 1 | CREATE SCHEMA playground_db 2 | AUTHORIZATION postgres; 3 | 4 | CREATE TABLE playground_db."users" 5 | ( 6 | id uuid NOT NULL, 7 | username character varying(255) COLLATE pg_catalog."default" NOT NULL, 8 | email character varying(255) COLLATE pg_catalog."default" NOT NULL, 9 | eth_address character varying(42) COLLATE pg_catalog."default", 10 | multisig_address character varying(42) COLLATE pg_catalog."default", 11 | transaction_hash character varying(255), 12 | node_address character varying(128) COLLATE pg_catalog."default", 13 | CONSTRAINT users_pkey PRIMARY KEY (id), 14 | CONSTRAINT uk_users__username UNIQUE (username) 15 | 16 | ) 17 | WITH ( 18 | OIDS = FALSE 19 | ) 20 | TABLESPACE pg_default; 21 | 22 | ALTER TABLE playground_db.users 23 | OWNER to postgres; 24 | 25 | CREATE TABLE playground_db."playground_snapshot" 26 | ( 27 | snapshot bytea 28 | ) 29 | WITH ( 30 | OIDS = FALSE 31 | ) 32 | TABLESPACE pg_default; 33 | 34 | ALTER TABLE playground_db.playground_snapshot 35 | OWNER to postgres; -------------------------------------------------------------------------------- /packages/simple-hub-server/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | services: 3 | postgres: 4 | build: . 5 | ports: 6 | - "5432:5432" 7 | -------------------------------------------------------------------------------- /packages/simple-hub-server/jest.config.js: -------------------------------------------------------------------------------- 1 | const rootConfig = require("../../jest.config"); 2 | 3 | module.exports = Object.assign( 4 | rootConfig, 5 | { 6 | "globalSetup": "/test/global-setup.jest.ts", 7 | "globalTeardown": "/test/global-teardown.jest.ts", 8 | "testEnvironment": "/test/node-test-environment.jest.js", 9 | } 10 | ); 11 | -------------------------------------------------------------------------------- /packages/simple-hub-server/registry.json: -------------------------------------------------------------------------------- 1 | { 2 | "data": [ 3 | { 4 | "type": "app", 5 | "id": { 6 | "3": "0x903217387B06a84F4dD0bEA565Ad8765Fc7cAA58", 7 | "42": "0x144F1A5C2db59B58f2c73d09A2acb27a57E47618" 8 | }, 9 | "attributes": { 10 | "name": "High Roller", 11 | "url": "https://high-roller-staging.counterfactual.com", 12 | "slug": "high-roller", 13 | "icon": "assets/images/logo.png" 14 | }, 15 | "relationships": {} 16 | }, 17 | { 18 | "type": "app", 19 | "id": { 20 | "3": "0xe40b051B8c3697D2cB0527c1d2405D26BE595DeC", 21 | "42": "0x3d9CbDd5e869638ca8A114289E42aeE8d0966B5b" 22 | }, 23 | "attributes": { 24 | "name": "Tic Tac Toe", 25 | "url": "https://tic-tac-toe-staging.netlify.com", 26 | "slug": "tic-tac-toe", 27 | "icon": "images/logo.png" 28 | }, 29 | "relationships": {} 30 | } 31 | ] 32 | } 33 | -------------------------------------------------------------------------------- /packages/simple-hub-server/registry.local.json: -------------------------------------------------------------------------------- 1 | { 2 | "data": [ 3 | { 4 | "type": "app", 5 | "id": { 6 | "3": "0x903217387B06a84F4dD0bEA565Ad8765Fc7cAA58", 7 | "42": "0x144F1A5C2db59B58f2c73d09A2acb27a57E47618" 8 | }, 9 | "attributes": { 10 | "name": "High Roller", 11 | "url": "http://localhost:3333", 12 | "slug": "high-roller", 13 | "icon": "assets/images/logo.png" 14 | }, 15 | "relationships": {} 16 | }, 17 | { 18 | "type": "app", 19 | "id": { 20 | "3": "0xe40b051B8c3697D2cB0527c1d2405D26BE595DeC", 21 | "42": "0x3d9CbDd5e869638ca8A114289E42aeE8d0966B5b" 22 | }, 23 | "attributes": { 24 | "name": "Tic Tac Toe", 25 | "url": "http://localhost:3000", 26 | "slug": "tic-tac-toe", 27 | "icon": "images/logo.png" 28 | }, 29 | "relationships": {} 30 | } 31 | ] 32 | } 33 | -------------------------------------------------------------------------------- /packages/simple-hub-server/scripts/heroku-postbuild.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | if [ ! -z "${IS_HEROKU_ENV}" ]; then 6 | echo "📟 Detected Heroku. Transpiling Typescript into JS for Node.JS to run on server." 7 | tsc -b tsconfig.heroku.json 8 | fi 9 | -------------------------------------------------------------------------------- /packages/simple-hub-server/scripts/postinstall.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | if [ ! -z "${IS_HEROKU_ENV}" ]; then 6 | echo "Deleting ./node_modules/websocket/.git -- incorrectly added installation artifact" 7 | rm -rf ./node_modules/websocket/.git 8 | fi 9 | -------------------------------------------------------------------------------- /packages/simple-hub-server/src/resources/app/processor.ts: -------------------------------------------------------------------------------- 1 | import { OperationProcessor } from "@ebryn/jsonapi-ts"; 2 | import fs from "fs"; 3 | import { Log } from "logepi"; 4 | import path from "path"; 5 | 6 | import Errors from "../../errors"; 7 | 8 | import App from "./resource"; 9 | 10 | export default class AppProcessor extends OperationProcessor { 11 | public resourceClass = App; 12 | 13 | public async get(): Promise { 14 | const filename = 15 | { 16 | development: "registry.local.json" 17 | }[process.env.STORE_PREFIX as string] || "registry.json"; 18 | 19 | try { 20 | const registry = JSON.parse( 21 | fs 22 | .readFileSync(path.resolve(__dirname, `../../../${filename}`)) 23 | .toString() 24 | ); 25 | 26 | Log.debug("Loaded App registry", { 27 | tags: { totalApps: registry.data.length, endpoint: "apps" } 28 | }); 29 | 30 | return registry.data.map((record: {}) => new App(record)); 31 | } catch { 32 | throw Errors.AppRegistryNotAvailable(); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /packages/simple-hub-server/src/resources/app/resource.ts: -------------------------------------------------------------------------------- 1 | import { Resource } from "@ebryn/jsonapi-ts"; 2 | 3 | export default class App extends Resource { 4 | static get type() { 5 | return "app"; 6 | } 7 | 8 | static attributes = { 9 | name: "", 10 | slug: "", 11 | icon: "", 12 | url: "" 13 | }; 14 | } 15 | -------------------------------------------------------------------------------- /packages/simple-hub-server/src/resources/heartbeat/processor.ts: -------------------------------------------------------------------------------- 1 | import { OperationProcessor } from "@ebryn/jsonapi-ts"; 2 | 3 | import Heartbeat from "./resource"; 4 | 5 | export default class HeartbeatProcessor extends OperationProcessor { 6 | public resourceClass = Heartbeat; 7 | 8 | public async get() { 9 | return [ 10 | { 11 | type: "heartbeat", 12 | attributes: { 13 | schemaVersion: String(process.env.PLAYGROUND_SCHEMA_VERSION), 14 | maintenanceMode: process.env.PLAYGROUND_MAINTENANCE_MODE === "true" 15 | }, 16 | relationships: {} 17 | } 18 | ] as Heartbeat[]; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /packages/simple-hub-server/src/resources/heartbeat/resource.ts: -------------------------------------------------------------------------------- 1 | import { Resource } from "@ebryn/jsonapi-ts"; 2 | 3 | export default class Heartbeat extends Resource { 4 | static get type() { 5 | return "heartbeat"; 6 | } 7 | 8 | static attributes = { 9 | schemaVersion: "", 10 | maintenanceMode: false 11 | }; 12 | } 13 | -------------------------------------------------------------------------------- /packages/simple-hub-server/src/resources/matchmaking-request/resource.ts: -------------------------------------------------------------------------------- 1 | import { Resource } from "@ebryn/jsonapi-ts"; 2 | 3 | export default class MatchmakingRequest extends Resource { 4 | static get type() { 5 | return "matchmakingRequest"; 6 | } 7 | 8 | static attributes = { 9 | intermediary: "", 10 | username: "", 11 | ethAddress: "", 12 | nodeAddress: "" 13 | }; 14 | // attributes: { 15 | // intermediary: string; 16 | // }; 17 | // relationships: { 18 | // users: {}; 19 | // }; 20 | } 21 | -------------------------------------------------------------------------------- /packages/simple-hub-server/src/resources/multisig-deploy/processor.ts: -------------------------------------------------------------------------------- 1 | import { Operation, OperationProcessor } from "@ebryn/jsonapi-ts"; 2 | 3 | import { getUser } from "../../db"; 4 | import { NodeWrapper } from "../../node"; 5 | import informSlack from "../../utils"; 6 | import User from "../user/resource"; 7 | 8 | import MultisigDeploy from "./resource"; 9 | 10 | export default class MultisigDeployProcessor extends OperationProcessor< 11 | MultisigDeploy 12 | > { 13 | public resourceClass = MultisigDeploy; 14 | 15 | public async add(op: Operation): Promise { 16 | const user = await getUser({ attributes: op.data.attributes } as Partial< 17 | User 18 | >); 19 | 20 | const { nodeAddress } = user.attributes; 21 | const { transactionHash } = await NodeWrapper.createStateChannelFor( 22 | nodeAddress as string 23 | ); 24 | 25 | informSlack( 26 | `📄 *MULTISIG_TX_BROADCASTED* (_${user.attributes.username}_) | Broadcasted multisig creation transaction .` 27 | ); 28 | 29 | return { 30 | type: "multisig-deploy", 31 | id: transactionHash 32 | } as MultisigDeploy; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /packages/simple-hub-server/src/resources/multisig-deploy/resource.ts: -------------------------------------------------------------------------------- 1 | import { Resource } from "@ebryn/jsonapi-ts"; 2 | 3 | export default class MultisigDeploy extends Resource { 4 | static get type() { 5 | return "multisigDeploy"; 6 | } 7 | 8 | static attributes = { 9 | ethAddress: "", 10 | transactionHash: "" 11 | }; 12 | } 13 | -------------------------------------------------------------------------------- /packages/simple-hub-server/src/resources/session-request/processor.ts: -------------------------------------------------------------------------------- 1 | import { Operation, OperationProcessor } from "@ebryn/jsonapi-ts"; 2 | import { sign } from "jsonwebtoken"; 3 | 4 | import { getUser } from "../../db"; 5 | import User from "../user/resource"; 6 | 7 | import SessionRequest from "./resource"; 8 | 9 | export default class SessionRequestProcessor extends OperationProcessor< 10 | SessionRequest 11 | > { 12 | public resourceClass = SessionRequest; 13 | 14 | public async add(op: Operation): Promise { 15 | const user = await getUser(op.data as User); 16 | 17 | user.attributes.token = sign( 18 | JSON.parse(JSON.stringify(user)), 19 | process.env.NODE_PRIVATE_KEY as string, 20 | { 21 | expiresIn: "1Y" 22 | } 23 | ); 24 | 25 | return user; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /packages/simple-hub-server/src/resources/session-request/resource.ts: -------------------------------------------------------------------------------- 1 | import { Resource } from "@ebryn/jsonapi-ts"; 2 | 3 | export default class SessionRequest extends Resource { 4 | static get type() { 5 | return "sessionRequest"; 6 | } 7 | 8 | static attributes = { 9 | username: "", 10 | email: "", 11 | ethAddress: "", 12 | multisigAddress: "", 13 | nodeAddress: "", 14 | token: "" 15 | }; 16 | } 17 | -------------------------------------------------------------------------------- /packages/simple-hub-server/src/resources/user/resource.ts: -------------------------------------------------------------------------------- 1 | import { Resource } from "@ebryn/jsonapi-ts"; 2 | 3 | export default class User extends Resource { 4 | static get type() { 5 | return "user"; 6 | } 7 | 8 | static attributes = { 9 | username: "", 10 | ethAddress: "", 11 | nodeAddress: "", 12 | email: "", 13 | multisigAddress: "", 14 | transactionHash: "", 15 | token: "" 16 | }; 17 | } 18 | 19 | export class MatchedUser extends Resource { 20 | static get type() { 21 | return "matchedUser"; 22 | } 23 | 24 | static attributes = { 25 | username: "", 26 | ethAddress: "", 27 | nodeAddress: "" 28 | }; 29 | } 30 | -------------------------------------------------------------------------------- /packages/simple-hub-server/src/types.ts: -------------------------------------------------------------------------------- 1 | export type AuthenticatedRequest = { 2 | headers: { 3 | authorization: string; 4 | }; 5 | }; 6 | 7 | export type JsonApiResource = { 8 | id?: string; 9 | type: string; 10 | attributes: { 11 | [key: string]: string | number | boolean; 12 | }; 13 | relationships?: { 14 | type: string; 15 | id: string; 16 | }; 17 | }; 18 | -------------------------------------------------------------------------------- /packages/simple-hub-server/src/utils.ts: -------------------------------------------------------------------------------- 1 | import Axios from "axios"; 2 | 3 | export default function informSlack(text: string) { 4 | if (process.env.SLACK_SUPPORT_WEBHOOK_URL) { 5 | try { 6 | Axios.post(process.env.SLACK_SUPPORT_WEBHOOK_URL, { text }); 7 | } catch {} 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /packages/simple-hub-server/test/global-teardown.jest.ts: -------------------------------------------------------------------------------- 1 | export default async function() { 2 | // @ts-ignore 3 | global.ganacheServer.close(); 4 | } 5 | -------------------------------------------------------------------------------- /packages/simple-hub-server/tsconfig.do-not-edit-merged.json: -------------------------------------------------------------------------------- 1 | /** 2 | * WARNING: This file is generated by combining the root level tsconfig with the 3 | * package level tsconfig. Ideally it should be auto-generated. It is necessary 4 | * because on Heroku we do not have access to the root level tsconfig. 5 | */ 6 | { 7 | "compilerOptions": { 8 | "allowJs": false, 9 | "allowSyntheticDefaultImports": true, 10 | "declaration": true, 11 | "emitDecoratorMetadata": true, 12 | "esModuleInterop": true, 13 | "experimentalDecorators": true, 14 | "forceConsistentCasingInFileNames": true, 15 | "lib": ["es2017", "dom", "esnext.asynciterable"], 16 | "module": "commonjs", 17 | "moduleResolution": "node", 18 | "noImplicitAny": false, 19 | "noImplicitReturns": true, 20 | "noLib": false, 21 | "noUnusedLocals": true, 22 | "outDir": "dist", 23 | "removeComments": true, 24 | "resolveJsonModule": true, 25 | "skipLibCheck": true, 26 | "sourceMap": true, 27 | "strict": true, 28 | "strictPropertyInitialization": false, 29 | "target": "es2017", 30 | "typeRoots": ["./node_modules/@counterfactual/typescript-typings/types"], 31 | "types": ["jsonwebtoken", "knex", "koa", "koa__cors", "uuid", "jest"] 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /packages/simple-hub-server/tsconfig.heroku.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.do-not-edit-merged.json", 3 | "include": ["src"] 4 | } 5 | -------------------------------------------------------------------------------- /packages/simple-hub-server/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "module": "commonjs", 5 | "outDir": "dist", 6 | "types": ["jsonwebtoken", "knex", "koa", "koa__cors", "uuid", "jest"], 7 | "typeRoots": [ 8 | "../../node_modules/@types", 9 | "../../node_modules/@counterfactual/typescript-typings/types" 10 | ], 11 | "strictPropertyInitialization": false 12 | }, 13 | "references": [ 14 | { "path": "../types" } 15 | ], 16 | } 17 | -------------------------------------------------------------------------------- /packages/simple-hub-server/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../../tslint.json"] 3 | } 4 | -------------------------------------------------------------------------------- /packages/specs/.gitignore: -------------------------------------------------------------------------------- 1 | build/* 2 | venv/* 3 | .vscode/* 4 | -------------------------------------------------------------------------------- /packages/specs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | SOURCEDIR = source 8 | BUILDDIR = build 9 | 10 | # Put it first so that "make" without argument is like "make help". 11 | help: 12 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 13 | 14 | .PHONY: help Makefile 15 | 16 | # Catch-all target: route all unknown targets to Sphinx using the new 17 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 18 | %: Makefile 19 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) -------------------------------------------------------------------------------- /packages/specs/README.md: -------------------------------------------------------------------------------- 1 | # [Counterfactual](https://counterfactual.com) In-Progress Specifications 2 | 3 | The specifications are currently a work-in-progress and are still being drafted. We welcome any pull requests, comments, or general feedback on the notes below. Additionally, you can chat with us on [our discord server](https://counterfactual.com/chat) if you'd like. 4 | 5 | ## Building 6 | 7 | See [CONTRIBUTING.md](source/CONTRIBUTING.md) for building instructions. -------------------------------------------------------------------------------- /packages/specs/requirements.txt: -------------------------------------------------------------------------------- 1 | sphinx_markdown_tables 2 | sphinxcontrib-mermaid 3 | recommonmark 4 | sphinx 5 | sphinx_rtd_theme 6 | pygments-lexer-solidity 7 | -------------------------------------------------------------------------------- /packages/specs/source/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | HTML files are built automatically by readthedocs. To build a local preview copy, a Makefile is provided that invokes sphinx. Install the dependencies and build a local preview by doing the following: 4 | 5 | ```shell 6 | python3 -m venv venv 7 | source venv/bin/activate 8 | pip3 install -r requirements.txt 9 | make html 10 | ``` 11 | -------------------------------------------------------------------------------- /packages/specs/source/diagrams/conditional-transaction-commitment.mmd: -------------------------------------------------------------------------------- 1 | graph TB 2 | 3 | subgraph Interpreter 4 | interpretOutcomeAndExecuteEffect["interpretOutcomeAndExecuteEffect(outcome, params)"] 5 | end 6 | 7 | subgraph ChallengeRegistry 8 | getOutcome["getOutcome(freeBalanceAppIdentityHash)"] 9 | getOutcome2["getOutcome(appIdentityHash)"] 10 | end 11 | 12 | subgraph ConditionalTransactionDelegateTarget 13 | executeEffectOfInterpretedAppOutcome 14 | executeEffectOfInterpretedAppOutcome 15 | -->|DELEGATECALL - Pass outcome and params to interpreter| interpretOutcomeAndExecuteEffect 16 | executeEffectOfInterpretedAppOutcome 17 | -->|STATICCALL - Fetch outcome for interpreter| getOutcome2 18 | executeEffectOfInterpretedAppOutcome 19 | -->|STATICCALL - Verify appIdentityHash in activeApps| getOutcome 20 | end 21 | 22 | subgraph Multisig 23 | execTransaction 24 | -->|DELEGATECALL - Delegate to delegate target to control funds |executeEffectOfInterpretedAppOutcome 25 | end 26 | -------------------------------------------------------------------------------- /packages/specs/source/diagrams/install-protocol-exchange.mmd: -------------------------------------------------------------------------------- 1 | sequenceDiagram 2 | 3 | Note over Initiating: Sign
ConditionalTransaction 4 | 5 | Initiating->>Responding: M1 6 | 7 | Note over Responding: Verify then
sign both 8 | 9 | Responding->>Initiating: M2 10 | 11 | Note over Initiating: Verify then sign
FreeBalanceSetState 12 | 13 | Initiating->>+Responding: M3 14 | 15 | Note over Responding: Verify 16 | 17 | Responding->>Initiating: M4 18 | -------------------------------------------------------------------------------- /packages/specs/source/diagrams/install-protocol-state.mmd: -------------------------------------------------------------------------------- 1 | graph LR 2 | 3 | ms-->ttt 4 | ms-->fb 5 | 6 | subgraph off-chain state 7 | fb["Free Balance (ETH)
versionNumber: k + 1
Alice has 9 ETH
Bob has 9 ETH"] 8 | ttt["TicTacToe App
versionNumber: 0
Winner gets 2 ETH
|   |   |   |
|   |   |   |
|   |   |   |
"] 9 | end 10 | 11 | subgraph on-chain state 12 | ms["AB Multisignature Wallet
Balance: 20 ETH"] 13 | end 14 | -------------------------------------------------------------------------------- /packages/specs/source/diagrams/install-virtual-app-exchange.mmd: -------------------------------------------------------------------------------- 1 | sequenceDiagram 2 | 3 | participant A as Initiating (A) 4 | participant I as Intermediary (I) 5 | participant B as Responding (B) 6 | 7 | Note over A: Sign AB
VirtualAppAgreement 8 | 9 | A->>I: M1 10 | 11 | Note over I: Sign BC
VirtualAppAgreement 12 | 13 | I->>B: M2 14 | 15 | Note over B: Countersign BC
VirtualAppAgreement
& sign BC
FreeBalanceActivation 16 | 17 | B->>I: M3 18 | 19 | Note over I: Countersign AB
VirtualAppAgreement
& sign AB
FreeBalanceActivation 20 | 21 | I->>A: M4 22 | 23 | Note over A: Countersign AB
FreeBalanceActivation
& sign AB
VirtualApp SetState
& AIB TLPT 24 | 25 | A->>I: M5 26 | 27 | Note over I: Countersign BC
FreeBalanceActivation
& sign AIB TLPT 28 | 29 | I->>B: M6 30 | 31 | Note over B: Countersign AB
VirtualApp SetState
& AIB TLPT 32 | 33 | B->>I: M7 34 | 35 | Note over I: Verify signatures 36 | 37 | I->>A: M8 38 | 39 | Note over A: Verify signatures 40 | -------------------------------------------------------------------------------- /packages/specs/source/diagrams/setstate-protocol-commitment.mmd: -------------------------------------------------------------------------------- 1 | graph LR 2 | subgraph ChallengeRegistry 3 | setState 4 | setState-->appIdentity:AppIdentity 5 | setState-->req:SignedStateHashUpdate 6 | end -------------------------------------------------------------------------------- /packages/specs/source/diagrams/setstate-protocol-exchange.mmd: -------------------------------------------------------------------------------- 1 | sequenceDiagram 2 | Alice->>+Bob: SetState 3 | Bob-->>-Alice: SetStateAck -------------------------------------------------------------------------------- /packages/specs/source/diagrams/setstate-protocol-state.mmd: -------------------------------------------------------------------------------- 1 | graph LR 2 | ms-->ttt 3 | ms-->fb 4 | subgraph off-chain state 5 | fb["Free Balance (ETH)
Local Nonce: k + 1
Alice has 9 ETH
Bob has 9 ETH"] 6 | ttt["TicTacToe App
Local Nonce: 1
Winner gets 2 ETH
| X |   |   |
|   |   |   |
|   |   |   |
"] 7 | end 8 | subgraph on-chain state 9 | ms["AB Multisignature Wallet
Balance: 20 ETH"] 10 | end -------------------------------------------------------------------------------- /packages/specs/source/diagrams/setup-commitment.mmd: -------------------------------------------------------------------------------- 1 | graph TB 2 | 3 | subgraph Interpreter 4 | interpretOutcomeAndExecuteEffect["interpretOutcomeAndExecuteEffect(outcome, params)"] 5 | end 6 | 7 | subgraph ChallengeRegistry 8 | getOutcome["getOutcome(freeBalanceAppIdentityHash)"] 9 | end 10 | 11 | subgraph ConditionalTransactionDelegateTarget 12 | executeEffectOfFreeBalance 13 | executeEffectOfFreeBalance 14 | -->|DELEGATECALL - Pass outcome and params to interpreter| interpretOutcomeAndExecuteEffect 15 | executeEffectOfFreeBalance 16 | -->|STATICCALL - Fetch outcome for interpreter| getOutcome 17 | end 18 | 19 | subgraph Multisig 20 | execTransaction 21 | -->|DELEGATECALL - Delegate to delegate target to control funds |executeEffectOfFreeBalance 22 | end 23 | -------------------------------------------------------------------------------- /packages/specs/source/diagrams/setup-protocol-exchange.mmd: -------------------------------------------------------------------------------- 1 | sequenceDiagram 2 | Alice->>+Bob: Setup 3 | Bob-->>-Alice: SetupAck -------------------------------------------------------------------------------- /packages/specs/source/diagrams/setup-protocol-state.mmd: -------------------------------------------------------------------------------- 1 | graph LR 2 | ms-->fb 3 | subgraph off-chain state 4 | fb["Free Balance (ETH)
 versionNumber: 0 
 Alice has 0 ETH 
 Bob has 0 ETH"]
5 |   end
6 |   subgraph on-chain state
7 |     ms["AB Multisignature Wallet 
 Balance: 0 ETH"]
8 |   end
9 | 


--------------------------------------------------------------------------------
/packages/specs/source/diagrams/takeaction-protocol-exchange.mmd:
--------------------------------------------------------------------------------
1 | sequenceDiagram
2 |     Alice->>+Bob: TakeAction
3 |     Bob-->>-Alice: TakeActionAck


--------------------------------------------------------------------------------
/packages/specs/source/diagrams/uninstall-protocol-commitment.mmd:
--------------------------------------------------------------------------------
1 | graph LR
2 |     subgraph ChallengeRegistry
3 |         setState
4 |         setState-->freeBalanceAppIdentity:AppIdentity
5 |         setState-->req:SignedStateHashUpdate
6 |     end
7 | 


--------------------------------------------------------------------------------
/packages/specs/source/diagrams/uninstall-protocol-exchange.mmd:
--------------------------------------------------------------------------------
1 | sequenceDiagram
2 |     Alice->>+Bob: Uninstall
3 |     Bob-->>-Alice: UninstallAck


--------------------------------------------------------------------------------
/packages/specs/source/diagrams/uninstall-protocol-state.mmd:
--------------------------------------------------------------------------------
 1 | graph LR
 2 |     ms-->fb
 3 |     subgraph stale-invalid state
 4 |       ttt["TicTacToe App 
versionNumber: 5
Winner gets 2 ETH
| X | O |   |
| O | X |   |
|   |   | X |
"] 5 | end 6 | subgraph off-chain state 7 | fb["Free Balance (ETH)
versionNumber: k + 1
Alice has 11 ETH
Bob has 9 ETH"] 8 | end 9 | subgraph on-chain state 10 | ms["AB Multisignature Wallet
Balance: 20 ETH"] 11 | end 12 | -------------------------------------------------------------------------------- /packages/specs/source/diagrams/uninstall-virtual-app-exchange.mmd: -------------------------------------------------------------------------------- 1 | sequenceDiagram 2 | participant Initiating 3 | participant Intermediary 4 | participant Responding 5 | Initiating->>Intermediary: M1 6 | Intermediary->>Responding: M2 7 | Responding->>Intermediary: M3 8 | Intermediary->>Initiating: M4 9 | Initiating->>Intermediary: M5 10 | Intermediary->>Responding: M6 11 | Responding->>Intermediary: M7 12 | Intermediary->>Initiating: M8 13 | -------------------------------------------------------------------------------- /packages/specs/source/diagrams/withdraw-exchange.mmd: -------------------------------------------------------------------------------- 1 | sequenceDiagram 2 | 3 | participant Initiating 4 | 5 | participant Responding 6 | 7 | Initiating->>Responding: M1 8 | 9 | Responding->>Initiating: M2 10 | 11 | Initiating->>Responding: M3 12 | 13 | Responding->>Initiating: M4 14 | 15 | Initiating->>Responding: M5 16 | -------------------------------------------------------------------------------- /packages/specs/source/index.rst: -------------------------------------------------------------------------------- 1 | .. cf-specs documentation master file, created by 2 | sphinx-quickstart on Mon Mar 25 14:33:18 2019. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Welcome to Counterfactual's documentation! 7 | ========================================== 8 | 9 | .. toctree:: 10 | :maxdepth: 2 11 | :caption: Contents: 12 | 13 | introduction.md 14 | app-definition.md 15 | adjudication-layer.md 16 | channel-networks.md 17 | peer-protocol.md 18 | protocols/setup.md 19 | protocols/install.md 20 | protocols/update.md 21 | protocols/uninstall.md 22 | protocols/cleanup.md 23 | protocols/install-virtual-app.md 24 | protocols/uninstall-virtual-app.md 25 | protocols/withdraw.md 26 | GLOSSARY.md 27 | CONTRIBUTING.md 28 | -------------------------------------------------------------------------------- /packages/types/.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | -------------------------------------------------------------------------------- /packages/types/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@counterfactual/types", 3 | "version": "0.0.45", 4 | "description": "TypeScript typings for common Counterfactual types", 5 | "main": "dist/index.js", 6 | "module": "dist/index.esm.js", 7 | "types": "dist/src/index.d.ts", 8 | "iife": "dist/index-iife.js", 9 | "license": "MIT", 10 | "engines": { 11 | "yarn": ">=1.17.3", 12 | "node": "^10 || ^12" 13 | }, 14 | "scripts": { 15 | "build": "tsc -b . && rollup -c" 16 | }, 17 | "devDependencies": { 18 | "ethers": "4.0.38", 19 | "rollup": "1.26.0", 20 | "rollup-plugin-typescript2": "0.24.3", 21 | "rpc-server": "0.0.1", 22 | "typescript": "3.5.3" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /packages/types/rollup.config.js: -------------------------------------------------------------------------------- 1 | import typescript from "rollup-plugin-typescript2"; 2 | 3 | import pkg from "./package.json"; 4 | 5 | export default [ 6 | { 7 | input: "src/index.ts", 8 | output: [ 9 | { 10 | file: pkg.main, 11 | format: "cjs" 12 | }, 13 | { 14 | file: pkg.module, 15 | format: "esm" 16 | }, 17 | { 18 | file: pkg.iife, 19 | format: "iife", 20 | name: "window.types" 21 | } 22 | ], 23 | plugins: [typescript()] 24 | } 25 | ]; 26 | -------------------------------------------------------------------------------- /packages/types/src/app-instance.ts: -------------------------------------------------------------------------------- 1 | import { BigNumberish } from "ethers/utils"; 2 | 3 | export type AppIdentity = { 4 | channelNonce: BigNumberish; 5 | participants: string[]; 6 | appDefinition: string; 7 | defaultTimeout: number; 8 | }; 9 | 10 | export type AppInterface = { 11 | addr: string; 12 | stateEncoding: string; 13 | actionEncoding: string | undefined; 14 | }; 15 | 16 | export type SignedStateHashUpdate = { 17 | appStateHash: string; 18 | versionNumber: number; 19 | timeout: number; 20 | signatures: string[]; 21 | }; 22 | -------------------------------------------------------------------------------- /packages/types/src/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | var AssetType; 4 | (function (AssetType) { 5 | AssetType[AssetType["ETH"] = 0] = "ETH"; 6 | AssetType[AssetType["ERC20"] = 1] = "ERC20"; 7 | })(AssetType = exports.AssetType || (exports.AssetType = {})); 8 | //# sourceMappingURL=index.js.map -------------------------------------------------------------------------------- /packages/types/src/simple-types.ts: -------------------------------------------------------------------------------- 1 | import { BigNumberish, Interface, ParamType } from "ethers/utils"; 2 | 3 | export type ABIEncoding = string; 4 | export type Address = string; 5 | 6 | // This is copied from the ethers definition of how an ABI is typed. 7 | // tslint:disable-next-line:prefer-array-literal 8 | export type ContractABI = Array | string | Interface; 9 | 10 | export type SolidityPrimitiveType = string | BigNumberish | boolean; 11 | 12 | // The application-specific state of an app instance, to be interpreted by the 13 | // app developer. We just treat it as an opaque blob; however since we pass this 14 | // around in protocol messages and include this in transaction data in challenges, 15 | // we impose some restrictions on the type; they must be serializable both as 16 | // JSON and as solidity structs. 17 | export type SolidityValueType = 18 | | SolidityPrimitiveType 19 | | SolidityABIEncoderV2Struct 20 | | SolidityABIEncoderV2SArray; 21 | 22 | type SolidityABIEncoderV2Struct = { 23 | [x: string]: SolidityValueType; 24 | }; 25 | 26 | // Ideally this should be a `type` not an `interface` but self-referencial 27 | // types is not supported: github.com/Microsoft/TypeScript/issues/6230 28 | interface SolidityABIEncoderV2SArray extends Array {} 29 | -------------------------------------------------------------------------------- /packages/types/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "composite": true, 5 | "outDir": "dist" 6 | }, 7 | "include": ["src/*.ts"] 8 | } 9 | -------------------------------------------------------------------------------- /packages/types/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../../tslint.json"] 3 | } 4 | -------------------------------------------------------------------------------- /packages/typescript-typings/README.md: -------------------------------------------------------------------------------- 1 | # [@counterfactual/typescript-typings](https://github.com/counterfactual/monorepo/packages/typescript-typings) 2 | 3 | Type repository for external packages used by Counterfactual's Typescript code. This is automatically imported by all packages in the monorepo as a kind of stub for any packages we need that are not typed in the way that we would like. 4 | 5 | ## Installation 6 | 7 | ```bash 8 | yarn add -D @counterfactual/typescript-typings 9 | ``` 10 | 11 | ## Usage 12 | 13 | Add the following line within an `compilerOptions` section of your `tsconfig.json` 14 | 15 | ```json 16 | "typeRoots": ["node_modules/@counterfactual/typescript-typings/types", "node_modules/@types"] 17 | ``` 18 | -------------------------------------------------------------------------------- /packages/typescript-typings/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@counterfactual/typescript-typings", 3 | "version": "0.1.3", 4 | "license": "MIT", 5 | "engines": { 6 | "yarn": ">=1.17.3", 7 | "node": "^10 || ^12" 8 | }, 9 | "description": "Counterfactual Types", 10 | "scripts": { 11 | "watch_without_deps": "tsc -w", 12 | "clean": "rm -rf scripts" 13 | }, 14 | "devDependencies": { 15 | "copyfiles": "2.1.1", 16 | "ethereum-waffle": "2.1.0", 17 | "ethers": "4.0.38", 18 | "tslint": "5.20.0", 19 | "typescript": "3.5.3" 20 | }, 21 | "dependencies": { 22 | "@types/node": "12.11.7", 23 | "truffle-contract": "4.0.31" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /packages/typescript-typings/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../../tslint.json"] 3 | } -------------------------------------------------------------------------------- /packages/typescript-typings/types/truffle/index.d.ts: -------------------------------------------------------------------------------- 1 | declare module "truffle" { 2 | import * as truffle from "truffle-contract"; 3 | 4 | interface ArtifactsGlobal { 5 | require(name: string): truffle.TruffleContract; 6 | } 7 | 8 | global { 9 | function contract( 10 | name: string, 11 | callback: (accounts: Array) => void 12 | ): void; 13 | const artifacts: ArtifactsGlobal; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /packages/typescript-typings/types/web3/index.d.ts: -------------------------------------------------------------------------------- 1 | // API matches version 0.20.3 of Web3 2 | 3 | declare var web3: { 4 | BigNumber: (num: string) => void; 5 | toHex: (string: string) => string; 6 | eth: { 7 | accounts: [string]; 8 | defaultBlock: number | string; 9 | getBalance: ( 10 | address: string, 11 | defaultBlock?: number | string, 12 | callback?: (error: Error, balance: BigNumber) => void 13 | ) => void; 14 | sendTransaction: ( 15 | transactionObject: { 16 | from: string | number; 17 | to?: string; 18 | value: number | string | BigNumber; 19 | }, 20 | callback: (err: Error, result: any) => void 21 | ) => void; 22 | }; 23 | personal: { 24 | sign: ( 25 | dataToSign: string, 26 | from: string | number, 27 | callback: (err: Error, signedData: string) => void 28 | ) => void; 29 | }; 30 | currentProvider: { 31 | enable: () => Promise; 32 | selectedAddress: string; 33 | }; 34 | version: { 35 | network: string; 36 | }; 37 | }; 38 | -------------------------------------------------------------------------------- /packages/wallet-ui/.env: -------------------------------------------------------------------------------- 1 | SKIP_PREFLIGHT_CHECK=true 2 | PORT=3334 3 | BROWSER=none 4 | -------------------------------------------------------------------------------- /packages/wallet-ui/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /packages/wallet-ui/public/assets/icon/arrow-active.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Arrow-Active 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /packages/wallet-ui/public/assets/icon/arrow.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Arrow 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /packages/wallet-ui/public/assets/icon/close.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/wallet-ui/public/assets/icon/error.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/wallet-ui/public/assets/icon/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/counterfactual/monorepo/f431e3ded7ffeec2e1e2c3cf7b4eeb1854e1154a/packages/wallet-ui/public/assets/icon/favicon.ico -------------------------------------------------------------------------------- /packages/wallet-ui/public/assets/icon/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/counterfactual/monorepo/f431e3ded7ffeec2e1e2c3cf7b4eeb1854e1154a/packages/wallet-ui/public/assets/icon/icon.png -------------------------------------------------------------------------------- /packages/wallet-ui/public/assets/icon/login.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /packages/wallet-ui/public/assets/icon/logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/counterfactual/monorepo/f431e3ded7ffeec2e1e2c3cf7b4eeb1854e1154a/packages/wallet-ui/public/assets/icon/logo.jpg -------------------------------------------------------------------------------- /packages/wallet-ui/public/assets/icon/menu-btn.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | Open Menu 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /packages/wallet-ui/public/assets/icon/register.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /packages/wallet-ui/public/assets/icon/wallet.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/wallet-ui/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/counterfactual/monorepo/f431e3ded7ffeec2e1e2c3cf7b4eeb1854e1154a/packages/wallet-ui/public/favicon.ico -------------------------------------------------------------------------------- /packages/wallet-ui/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": ".", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /packages/wallet-ui/src/App.scss: -------------------------------------------------------------------------------- 1 | @import "./styles/variables"; 2 | @import "./styles/reset"; 3 | @import "./styles/box-sizing"; 4 | @import "./styles/layout"; 5 | @import "./styles/typography"; 6 | @import "./styles/responsive"; 7 | 8 | /* 9 | Global App CSS 10 | ---------------------- 11 | Use this file for styles that should be applied to all components. 12 | For example, "font-family" within the "body" selector is a CSS property 13 | most apps will want applied to all components. 14 | 15 | Any global CSS variables should also be applied here. 16 | */ 17 | @import url("https://rsms.me/inter/inter.css"); 18 | 19 | html, 20 | .button, 21 | .btn { 22 | font-family: "Inter", sans-serif; 23 | } 24 | @supports (font-variation-settings: normal) { 25 | html, 26 | .button, 27 | .btn { 28 | font-family: "Inter var", sans-serif; 29 | } 30 | } 31 | 32 | html { 33 | font-size: 15px; 34 | box-sizing: border-box; 35 | -webkit-font-smoothing: antialiased; 36 | color: #252525; 37 | } 38 | 39 | body { 40 | margin: 0; 41 | padding: 0; 42 | } 43 | 44 | html, 45 | body { 46 | height: 100%; 47 | } 48 | -------------------------------------------------------------------------------- /packages/wallet-ui/src/App.test.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom"; 3 | import { Provider } from "react-redux"; 4 | 5 | import App from "./App"; 6 | import store from "./store/store"; 7 | import { EthereumService } from "./providers/EthereumService"; 8 | import { Web3Provider } from "ethers/providers"; 9 | import EthereumMock from "./store/test-utils/ethereum.mock"; 10 | 11 | window.ethereum = new EthereumMock(); 12 | const provider = new Web3Provider(window.ethereum); 13 | const signer = provider.getSigner(); 14 | 15 | const context = { provider, signer }; 16 | 17 | it("renders without crashing", () => { 18 | const div = document.createElement("div"); 19 | ReactDOM.render( 20 | 21 | 22 | 23 | 24 | , 25 | div 26 | ); 27 | ReactDOM.unmountComponentAtNode(div); 28 | }); 29 | -------------------------------------------------------------------------------- /packages/wallet-ui/src/components/account/index.ts: -------------------------------------------------------------------------------- 1 | import AccountContext from "./account-context/AccountContext"; 2 | 3 | export { AccountContext }; 4 | -------------------------------------------------------------------------------- /packages/wallet-ui/src/components/channel/channel-tree/ChannelTree.scss: -------------------------------------------------------------------------------- 1 | .channel-tree { 2 | width: 100%; 3 | display: block; 4 | list-style: none; 5 | padding-left: 0; 6 | margin-left: 0; 7 | margin-top: 0; 8 | 9 | .channel-tree .channel-node-wrapper { 10 | padding-left: 32px; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /packages/wallet-ui/src/components/channel/channel-tree/ChannelTree.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | import "./ChannelTree.scss"; 4 | 5 | type ChannelTreeProps = { 6 | children: React.ReactNode; 7 | }; 8 | 9 | const ChannelTree: React.FC = ({ children }) => ( 10 |
    {children}
11 | ); 12 | 13 | export { ChannelTree }; 14 | -------------------------------------------------------------------------------- /packages/wallet-ui/src/components/channel/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./channel-tree/ChannelTree"; 2 | export * from "./channel-node/ChannelNode"; 3 | -------------------------------------------------------------------------------- /packages/wallet-ui/src/components/form/form-button/FormButton.scss: -------------------------------------------------------------------------------- 1 | @import "../../../styles/variables"; 2 | @import "../../../styles/button"; 3 | 4 | .button { 5 | // width: 100%; 6 | font-size: 110%; 7 | font-weight: 500; 8 | background-color: $c-blue; 9 | border-radius: 2px; 10 | color: $c-white; 11 | padding: $rhythm $rhythm; 12 | display: block; 13 | margin-left: auto; 14 | margin-right: auto; 15 | margin-bottom: $rhythm; 16 | border-width: 0; 17 | 18 | &.full-width { 19 | width: 100%; 20 | } 21 | 22 | &:not(:disabled) { 23 | &:hover { 24 | background-color: $c-bluealt; 25 | } 26 | } 27 | 28 | &:disabled { 29 | cursor: default; 30 | opacity: 0.68; 31 | } 32 | 33 | &.button--secondary { 34 | background-color: $c-darkgrey; 35 | 36 | &:hover { 37 | background-color: $c-darkergrey; 38 | } 39 | } 40 | 41 | widget-spinner { 42 | margin-right: $rhythm * 2; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /packages/wallet-ui/src/components/form/form-button/FormButton.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { WidgetSpinner } from "../../widget"; 3 | import "./FormButton.scss"; 4 | 5 | export type FormButtonProps = { 6 | className?: string; 7 | name: string; 8 | spinner?: boolean; 9 | children?: React.ReactNode; 10 | disabled?: boolean; 11 | onClick?: 12 | | ((event: React.MouseEvent) => void) 13 | | undefined; 14 | type?: "submit" | "button"; 15 | }; 16 | 17 | const FormButton: React.FC = ({ 18 | children, 19 | onClick, 20 | name, 21 | disabled = false, 22 | spinner = false, 23 | className = "button", 24 | type = "button" 25 | }: FormButtonProps) => { 26 | return ( 27 | 37 | ); 38 | }; 39 | 40 | export { FormButton }; 41 | -------------------------------------------------------------------------------- /packages/wallet-ui/src/components/form/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./form-button/FormButton"; 2 | export * from "./form-input/FormInput"; 3 | -------------------------------------------------------------------------------- /packages/wallet-ui/src/components/layout/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./layout-header/LayoutHeader"; 2 | -------------------------------------------------------------------------------- /packages/wallet-ui/src/components/layout/layout-header/LayoutHeader.scss: -------------------------------------------------------------------------------- 1 | @import "../../../styles/variables"; 2 | 3 | .header { 4 | border-bottom: 1px solid $c-mediumgrey; 5 | background-color: $c-white; 6 | width: 100%; 7 | } 8 | 9 | .header-content { 10 | display: flex; 11 | align-items: center; 12 | height: 100%; 13 | padding: $rhythm / 4 $rhythm * 2; 14 | } 15 | 16 | .logo-container { 17 | width: auto; 18 | } 19 | 20 | .context-container { 21 | width: 100%; 22 | display: flex; 23 | height: 100%; 24 | justify-content: flex-end; 25 | } 26 | -------------------------------------------------------------------------------- /packages/wallet-ui/src/components/layout/layout-header/LayoutHeader.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | import { WidgetLogo } from "../../widget"; 4 | import { AccountContext } from "../../account"; 5 | 6 | import "./LayoutHeader.scss"; 7 | import { RouteComponentProps } from "react-router-dom"; 8 | 9 | const LayoutHeader: React.FC = props => { 10 | return ( 11 |
12 | 24 |
25 | ); 26 | }; 27 | 28 | export { LayoutHeader }; 29 | -------------------------------------------------------------------------------- /packages/wallet-ui/src/components/widget/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./widget-logo/WidgetLogo"; 2 | export * from "./widget-card/WidgetCard"; 3 | export * from "./widget-error-message/WidgetErrorMessage"; 4 | export * from "./widget-header/WidgetHeader"; 5 | export * from "./widget-tooltip/WidgetTooltip"; 6 | export * from "./widget-screen/WidgetScreen"; 7 | export * from "./widget-spinner/WidgetSpinner"; 8 | -------------------------------------------------------------------------------- /packages/wallet-ui/src/components/widget/widget-card/WidgetCard.scss: -------------------------------------------------------------------------------- 1 | @import "../../../styles/variables"; 2 | 3 | .card { 4 | background-color: $c-white; 5 | padding: $rhythm * 2; 6 | box-shadow: rgba(50, 50, 93, 0.11) 0px 4px 6px 0px; 7 | } 8 | -------------------------------------------------------------------------------- /packages/wallet-ui/src/components/widget/widget-card/WidgetCard.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | import "./WidgetCard.scss"; 4 | 5 | const WidgetCard: React.FC = ({ children }) => ( 6 |
{children}
7 | ); 8 | 9 | export { WidgetCard }; 10 | -------------------------------------------------------------------------------- /packages/wallet-ui/src/components/widget/widget-error-message/WidgetErrorMessage.scss: -------------------------------------------------------------------------------- 1 | @import "../../../styles/ui"; 2 | 3 | .widget-error-message { 4 | display: flex; 5 | height: 100%; 6 | align-items: center; 7 | padding: 0 10px; 8 | margin-right: 10px; 9 | color: $c-red; 10 | } 11 | -------------------------------------------------------------------------------- /packages/wallet-ui/src/components/widget/widget-error-message/WidgetErrorMessage.test.tsx: -------------------------------------------------------------------------------- 1 | import Enzyme from "enzyme"; 2 | import Adapter from "enzyme-adapter-react-16"; 3 | import React from "react"; 4 | import { 5 | WidgetErrorMessage, 6 | WidgetErrorMessageProps 7 | } from "./WidgetErrorMessage"; 8 | 9 | Enzyme.configure({ adapter: new Adapter() }); 10 | 11 | describe("", () => { 12 | let node: Enzyme.ShallowWrapper; 13 | 14 | const props = { 15 | error: { primary: "primary_string", secondary: "secondary_string" } 16 | } as WidgetErrorMessageProps; 17 | 18 | beforeEach(() => { 19 | node = Enzyme.shallow(); 20 | }); 21 | 22 | it("shows the primary and secondary error strings", () => { 23 | expect(node.find("WidgetTooltip").prop("message")).toBe( 24 | props.error.secondary 25 | ); 26 | expect(node.find(".widget-error-message").text()).toBe(props.error.primary); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /packages/wallet-ui/src/components/widget/widget-error-message/WidgetErrorMessage.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { WidgetTooltip } from "../widget-tooltip/WidgetTooltip"; 3 | 4 | export type WidgetErrorMessageProps = { 5 | error: { 6 | primary?: string; 7 | secondary?: string; 8 | }; 9 | }; 10 | 11 | const WidgetErrorMessage: React.FC = ({ error }) => 12 | error ? ( 13 | 14 |
{error.primary}
15 |
16 | ) : null; 17 | 18 | export { WidgetErrorMessage }; 19 | -------------------------------------------------------------------------------- /packages/wallet-ui/src/components/widget/widget-header/WidgetHeader.scss: -------------------------------------------------------------------------------- 1 | @import "../../../styles/variables"; 2 | 3 | .widget-header { 4 | font-size: 120%; 5 | font-weight: 500; 6 | text-align: center; 7 | margin: $rhythm * 2 0 $rhythm * 4; 8 | } 9 | -------------------------------------------------------------------------------- /packages/wallet-ui/src/components/widget/widget-header/WidgetHeader.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | import "./WidgetHeader.scss"; 4 | 5 | const WidgetHeader: React.FC = ({ children }) => ( 6 |

{children}

7 | ); 8 | 9 | export { WidgetHeader }; 10 | -------------------------------------------------------------------------------- /packages/wallet-ui/src/components/widget/widget-logo/WidgetLogo.scss: -------------------------------------------------------------------------------- 1 | @import "../../../styles/variables"; 2 | @import "../../../styles/typography"; 3 | 4 | .logo { 5 | margin: 10px; 6 | border: none; 7 | font-size: $f-base; 8 | 9 | &.logo--icon-only { 10 | border: 0; 11 | } 12 | 13 | a { 14 | color: inherit; 15 | display: flex; 16 | align-items: center; 17 | font-size: 18px; 18 | text-decoration: none; 19 | } 20 | 21 | img { 22 | height: 2.5rem; 23 | border-radius: 4px; 24 | } 25 | 26 | span { 27 | @include f-logo; 28 | display: flex; 29 | align-items: center; 30 | justify-content: center; 31 | width: 100%; 32 | font-size: 0.7rem; 33 | padding: 0 $rhythm; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /packages/wallet-ui/src/components/widget/widget-logo/WidgetLogo.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Link } from "react-router-dom"; 3 | 4 | import "./WidgetLogo.scss"; 5 | 6 | export type WidgetLogoProps = { 7 | caption?: string; 8 | linkToHome?: boolean; 9 | }; 10 | 11 | const WidgetLogo: React.FC = ({ 12 | caption = "Wallet", 13 | linkToHome = true 14 | }: WidgetLogoProps) => { 15 | const logo = ( 16 | 17 | Counterfactual 18 | {caption ? {caption} : null} 19 | 20 | ); 21 | 22 | return ( 23 |

24 | {linkToHome ? ( 25 | 26 | {logo} 27 | 28 | ) : ( 29 | logo 30 | )} 31 |

32 | ); 33 | }; 34 | 35 | export { WidgetLogo }; 36 | -------------------------------------------------------------------------------- /packages/wallet-ui/src/components/widget/widget-spinner/WidgetSpinner.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import "./WidgetSpinner.scss"; 3 | 4 | export type WidgetSpinnerProps = { 5 | visible?: boolean; 6 | type?: "circle" | "dots"; 7 | color?: "black" | "white"; 8 | content?: React.ReactNode; 9 | }; 10 | 11 | const WidgetSpinner: React.FC = ({ 12 | visible = false, 13 | type = "circle", 14 | color = "black", 15 | content 16 | }: WidgetSpinnerProps) => { 17 | if (type === "circle") { 18 | return ( 19 |
24 | ); 25 | } 26 | 27 | if (type === "dots") { 28 | return ( 29 |
34 |
35 |
36 |
37 |
38 |
39 | {content} 40 |
41 | ); 42 | } 43 | 44 | return null; 45 | }; 46 | export { WidgetSpinner }; 47 | -------------------------------------------------------------------------------- /packages/wallet-ui/src/components/widget/widget-tooltip/WidgetTooltip.scss: -------------------------------------------------------------------------------- 1 | @import "../../../styles/variables"; 2 | 3 | .widget-tooltip-message { 4 | display: none; 5 | position: absolute; 6 | width: 200px; 7 | margin-top: 10px; 8 | background: $c-black; 9 | color: $c-white; 10 | padding: 12px; 11 | font-size: 0.75rem; 12 | border-radius: 2px; 13 | box-shadow: 1px 2px 4px $c-darkgrey; 14 | 15 | &.toLeft { 16 | right: 0px; 17 | &:before { 18 | right: 20px; 19 | left: auto; 20 | } 21 | } 22 | 23 | &:before { 24 | position: absolute; 25 | width: 10px; 26 | height: 10px; 27 | background-color: $c-black; 28 | transform: rotateZ(45deg); 29 | top: -5px; 30 | left: 20px; 31 | content: ""; 32 | } 33 | } 34 | 35 | .widget-tooltip { 36 | position: relative; 37 | height: 100%; 38 | 39 | &:hover { 40 | .widget-tooltip-message { 41 | display: block; 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /packages/wallet-ui/src/components/widget/widget-tooltip/WidgetTooltip.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import "./WidgetTooltip.scss"; 3 | 4 | export type WidgetTooltipProps = { 5 | children?: React.ReactNode; 6 | message?: string; 7 | toLeft?: boolean; 8 | }; 9 | 10 | const WidgetTooltip: React.FC = ({ 11 | children, 12 | message = "", 13 | toLeft = false 14 | }: WidgetTooltipProps) => { 15 | return ( 16 |
17 | {children} 18 | {message ? ( 19 |
20 | {message} 21 |
22 | ) : ( 23 | undefined 24 | )} 25 |
26 | ); 27 | }; 28 | 29 | export { WidgetTooltip }; 30 | -------------------------------------------------------------------------------- /packages/wallet-ui/src/index.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom"; 3 | import { Provider } from "react-redux"; 4 | import { Web3Provider } from "ethers/providers"; 5 | 6 | import App from "./App"; 7 | import store from "./store/store"; 8 | import { EthereumService } from "./providers/EthereumService"; 9 | 10 | const provider = new Web3Provider(window.ethereum); 11 | const signer = provider.getSigner(); 12 | 13 | const context = { provider, signer }; 14 | 15 | ReactDOM.render( 16 | 17 | 18 | 19 | 20 | , 21 | document.getElementById("root") 22 | ); 23 | -------------------------------------------------------------------------------- /packages/wallet-ui/src/pages/account-balance/AccountBalance.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { RouteComponentProps } from "react-router-dom"; 3 | import AccountDeposit from "../account-deposit/AccountDeposit"; 4 | import AccountWithdraw from "../account-withdraw/AccountWithdraw"; 5 | 6 | const AccountBalance: React.FC = props => { 7 | return ( 8 | <> 9 | 10 | 11 | 12 | ); 13 | }; 14 | 15 | export default AccountBalance; 16 | -------------------------------------------------------------------------------- /packages/wallet-ui/src/pages/account-deposit/AccountDeposit.scss: -------------------------------------------------------------------------------- 1 | @import "../../styles/variables"; 2 | 3 | .details { 4 | color: $c-darkgrey; 5 | font-size: 90%; 6 | margin-bottom: $rhythm * 3; 7 | } 8 | 9 | .input--balance { 10 | .unit { 11 | color: $c-blue; 12 | } 13 | } 14 | 15 | .balance-label { 16 | display: flex; 17 | justify-content: space-between; 18 | } 19 | -------------------------------------------------------------------------------- /packages/wallet-ui/src/pages/account-registration/AccountRegistration.context.json: -------------------------------------------------------------------------------- 1 | { 2 | "props": { 3 | "userState": { 4 | "user": { 5 | "id": "id", 6 | "ethAddress": "0x0", 7 | "multisigAddress": "0x0", 8 | "nodeAddress": "xpub1", 9 | "username": "foo", 10 | "email": "foo@bar.co" 11 | }, 12 | "error": { 13 | "message": "", 14 | "code": "", 15 | "field": "" 16 | }, 17 | "status": "" 18 | }, 19 | "counterfactualBalance": "0x0de0b6b3a7640000", 20 | "ethAddress": "0x0", 21 | "match": { 22 | "isExact": true, 23 | "params": {}, 24 | "path": "/", 25 | "url": "http://localhost/" 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /packages/wallet-ui/src/pages/account-registration/AccountRegistration.scss: -------------------------------------------------------------------------------- 1 | @import "../../styles/variables"; 2 | 3 | .smallprint { 4 | margin: $rhythm * 2 0 $rhythm * 2 0; 5 | font-size: 0.78rem; 6 | font-weight: 400; 7 | color: $c-darkgrey; 8 | } 9 | -------------------------------------------------------------------------------- /packages/wallet-ui/src/pages/account-withdraw/AccountWithdraw.scss: -------------------------------------------------------------------------------- 1 | @import "../../styles/variables"; 2 | 3 | .details { 4 | color: $c-darkgrey; 5 | font-size: 90%; 6 | margin-bottom: $rhythm * 3; 7 | } 8 | 9 | .input--balance { 10 | .unit { 11 | color: $c-blue; 12 | } 13 | } 14 | 15 | .balance-label { 16 | display: flex; 17 | justify-content: space-between; 18 | } 19 | -------------------------------------------------------------------------------- /packages/wallet-ui/src/pages/channels/Channels.scss: -------------------------------------------------------------------------------- 1 | @import "../../styles/variables"; 2 | 3 | .channel-trees { 4 | flex: 1; 5 | } 6 | 7 | .button-discover-apps { 8 | display: block; 9 | margin: auto; 10 | font-size: 1.1rem; 11 | padding: $rhythm $rhythm * 4; 12 | font-weight: 400; 13 | margin-bottom: $rhythm * 2; 14 | justify-self: flex-end; 15 | } 16 | -------------------------------------------------------------------------------- /packages/wallet-ui/src/pages/index.ts: -------------------------------------------------------------------------------- 1 | import AccountBalance from "./account-balance/AccountBalance"; 2 | import AccountDeposit from "./account-deposit/AccountDeposit"; 3 | import AccountRegistration from "./account-registration/AccountRegistration"; 4 | import AccountWithdraw from "./account-withdraw/AccountWithdraw"; 5 | import Channels from "./channels/Channels"; 6 | import Welcome from "./welcome/Welcome"; 7 | 8 | export { 9 | AccountBalance, 10 | AccountRegistration, 11 | AccountDeposit, 12 | AccountWithdraw, 13 | Welcome, 14 | Channels 15 | }; 16 | -------------------------------------------------------------------------------- /packages/wallet-ui/src/pages/welcome/Welcome.scss: -------------------------------------------------------------------------------- 1 | @import "../../styles/variables"; 2 | 3 | .copy { 4 | margin-bottom: $rhythm * 2; 5 | font-weight: 300; 6 | font-size: 1.2rem; 7 | color: $c-darkergrey; 8 | } 9 | -------------------------------------------------------------------------------- /packages/wallet-ui/src/providers/EthereumService.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { EthereumServiceContext } from "../types"; 3 | 4 | const EthereumService = React.createContext( 5 | {} as EthereumServiceContext 6 | ); 7 | 8 | export { EthereumService }; 9 | -------------------------------------------------------------------------------- /packages/wallet-ui/src/react-app-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /packages/wallet-ui/src/setupTests.ts: -------------------------------------------------------------------------------- 1 | import { GlobalWithFetchMock } from "jest-fetch-mock"; 2 | 3 | const customGlobal: GlobalWithFetchMock = global as GlobalWithFetchMock; 4 | customGlobal.fetch = require("jest-fetch-mock"); 5 | customGlobal.fetchMock = customGlobal.fetch; 6 | -------------------------------------------------------------------------------- /packages/wallet-ui/src/store/channels/channels.mock.ts: -------------------------------------------------------------------------------- 1 | import { Action } from "redux"; 2 | import { ThunkAction } from "redux-thunk"; 3 | import { 4 | ActionType, 5 | ApplicationState, 6 | ChannelsMap, 7 | ChannelsState, 8 | StoreAction 9 | } from "../types"; 10 | 11 | export const initialState = { 12 | channels: {} as ChannelsMap 13 | } as ChannelsState; 14 | 15 | export const getAllChannels = (): ThunkAction< 16 | void, 17 | ApplicationState, 18 | null, 19 | Action 20 | > => async dispatch => { 21 | try { 22 | dispatch({ 23 | data: { channels: {} as ChannelsMap } as ChannelsState, 24 | type: ActionType.ChannelsGetAll 25 | }); 26 | } catch (e) { 27 | const error = e as Error; 28 | dispatch({ 29 | data: { error: { message: error.message } } as ChannelsState, 30 | type: ActionType.ChannelsError 31 | }); 32 | } 33 | }; 34 | 35 | export const reducers = function( 36 | state = initialState, 37 | action: StoreAction 38 | ) { 39 | switch (action.type) { 40 | case ActionType.ChannelsGetAll: 41 | case ActionType.ChannelsError: 42 | return { 43 | ...state, 44 | ...action.data 45 | }; 46 | default: 47 | return state; 48 | } 49 | }; 50 | -------------------------------------------------------------------------------- /packages/wallet-ui/src/store/store.mock.ts: -------------------------------------------------------------------------------- 1 | import { applyMiddleware, combineReducers, compose, createStore } from "redux"; 2 | import ReduxThunk from "redux-thunk"; 3 | import { reducers as ChannelsState } from "./channels/channels"; 4 | import { ApplicationState, StoreAction } from "./types"; 5 | import { reducers as UserState } from "./user/user"; 6 | import { reducers as WalletState } from "./wallet/wallet"; 7 | 8 | export default createStore( 9 | combineReducers>({ 10 | UserState, 11 | WalletState, 12 | ChannelsState 13 | }), 14 | compose(applyMiddleware(ReduxThunk)) 15 | ); 16 | -------------------------------------------------------------------------------- /packages/wallet-ui/src/store/store.ts: -------------------------------------------------------------------------------- 1 | import { applyMiddleware, combineReducers, compose, createStore } from "redux"; 2 | import ReduxThunk from "redux-thunk"; 3 | import { reducers as ChannelsState } from "./channels/channels"; 4 | import { ApplicationState, StoreAction } from "./types"; 5 | import { reducers as UserState } from "./user/user"; 6 | import { reducers as WalletState } from "./wallet/wallet"; 7 | 8 | export default createStore( 9 | combineReducers>({ 10 | UserState, 11 | WalletState, 12 | ChannelsState 13 | }), 14 | compose(applyMiddleware(ReduxThunk)) 15 | ); 16 | -------------------------------------------------------------------------------- /packages/wallet-ui/src/store/test-utils/hub-api-client.mock.ts: -------------------------------------------------------------------------------- 1 | import { User } from "../types"; 2 | import { USER_ID_MOCK, USER_MOCK_DATA } from "../user/user.mock"; 3 | import { TRANSACTION_MOCK_HASH } from "./ethereum.mock"; 4 | 5 | export const postUser = (user: User = USER_MOCK_DATA) => 6 | JSON.stringify({ 7 | type: "user", 8 | data: { 9 | id: USER_ID_MOCK, 10 | attributes: { ...user }, 11 | relationships: {} 12 | } 13 | }); 14 | 15 | export const postUserWithoutUsername = () => 16 | JSON.stringify({ 17 | status: 400, 18 | code: "username_required" 19 | }); 20 | 21 | export const postMultisigDeploy = () => 22 | JSON.stringify({ 23 | type: "multisig-deploy", 24 | data: { 25 | id: TRANSACTION_MOCK_HASH 26 | } 27 | }); 28 | 29 | export const postSessionWithoutUser = () => 30 | JSON.stringify({ 31 | status: 422, 32 | code: "invalid_token" 33 | }); 34 | -------------------------------------------------------------------------------- /packages/wallet-ui/src/store/test-utils/json-rpc-signer.mock.ts: -------------------------------------------------------------------------------- 1 | import { randomBytes } from "crypto"; 2 | import { Arrayish } from "ethers/utils"; 3 | 4 | export default class JsonRpcSignerMock { 5 | async signMessage(message: Arrayish): Promise { 6 | return `0x${randomBytes(message.length).toString("hex")}`; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /packages/wallet-ui/src/store/test-utils/web3provider.mock.ts: -------------------------------------------------------------------------------- 1 | import { JsonRpcSigner, Web3Provider } from "ethers/providers"; 2 | import { BigNumber, Network } from "ethers/utils"; 3 | import { ETHEREUM_MOCK_BALANCE, MOCK_NETWORK } from "./ethereum.mock"; 4 | import JsonRpcSignerMock from "./json-rpc-signer.mock"; 5 | 6 | export default class Web3ProviderMock extends Web3Provider { 7 | // @ts-ignore 8 | async getBalance(ethAddress: string): Promise { 9 | return ETHEREUM_MOCK_BALANCE; 10 | } 11 | 12 | getSigner(): JsonRpcSigner { 13 | return new JsonRpcSignerMock() as JsonRpcSigner; 14 | } 15 | 16 | async getNetwork() { 17 | return MOCK_NETWORK as Network; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /packages/wallet-ui/src/styles/_box-sizing.scss: -------------------------------------------------------------------------------- 1 | // apply a natural box layout model to all elements, but allowing components to change 2 | * { 3 | box-sizing: border-box; 4 | 5 | &:before, 6 | &:after { 7 | box-sizing: border-box; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /packages/wallet-ui/src/styles/_button.scss: -------------------------------------------------------------------------------- 1 | .btn { 2 | background-color: $c-blue; 3 | color: $c-white; 4 | border-radius: 4px; 5 | padding: 8px 16px; 6 | min-width: 80px; 7 | min-height: 30px; 8 | box-shadow: 1px 2px 4px $c-mediumgrey; 9 | 10 | &:not(:disabled) { 11 | &:hover { 12 | background-color: $c-bluealt; 13 | } 14 | } 15 | 16 | &:disabled { 17 | background-color: $c-bluelightened; 18 | } 19 | 20 | .icon { 21 | height: 16px; 22 | padding-right: 10px; 23 | position: relative; 24 | top: 2px; 25 | } 26 | } 27 | 28 | .btn-secondary { 29 | border: 1px solid $c-blue; 30 | color: $c-blue; 31 | background-color: $c-white; 32 | box-shadow: none; 33 | 34 | &:not(:disabled) { 35 | &:hover { 36 | background-color: $c-white; 37 | box-shadow: 0px 0px 3px $c-bluelightened inset; 38 | } 39 | } 40 | } 41 | 42 | .btn-alternate { 43 | background-color: $c-darkgrey; 44 | color: $c-white; 45 | 46 | &:not(:disabled) { 47 | &:hover { 48 | background-color: $c-lessdarkgrey; 49 | } 50 | } 51 | } 52 | 53 | .btn-container { 54 | display: flex; 55 | flex-direction: row; 56 | .btn { 57 | margin-right: 10px; 58 | &:last-child { 59 | margin-right: 0; 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /packages/wallet-ui/src/styles/_layout.scss: -------------------------------------------------------------------------------- 1 | .wrapper { 2 | display: flex; 3 | flex-direction: column; 4 | min-height: 100vh; 5 | transition: transform 150ms; 6 | 7 | &--open { 8 | // on menu-btn click 9 | transform: translateX(-15rem); 10 | } 11 | 12 | &__content { 13 | display: flex; 14 | flex-grow: 1; 15 | } 16 | } 17 | 18 | .section { 19 | padding: $rhythm / 4 $rhythm * 3; 20 | 21 | &.fill { 22 | width: 100%; 23 | display: flex; 24 | flex-direction: column; 25 | } 26 | } 27 | 28 | .container { 29 | max-width: $site-width; 30 | margin: 0 auto; 31 | } 32 | 33 | ul { 34 | list-style-type: disc; 35 | padding-left: $rhythm * 3; 36 | } 37 | -------------------------------------------------------------------------------- /packages/wallet-ui/src/styles/_reset.scss: -------------------------------------------------------------------------------- 1 | h1, 2 | h2, 3 | h3, 4 | h4, 5 | h5, 6 | h6, 7 | p { 8 | margin: 1rem 0; 9 | } 10 | 11 | ul { 12 | margin: 1rem 0; 13 | padding-left: 0; 14 | list-style: none; 15 | } 16 | 17 | button { 18 | background: none; 19 | border: 0; 20 | cursor: pointer; 21 | } 22 | 23 | a { 24 | text-decoration: none; 25 | color: $c-blue; 26 | cursor: pointer; 27 | } -------------------------------------------------------------------------------- /packages/wallet-ui/src/styles/_responsive.scss: -------------------------------------------------------------------------------- 1 | @media screen and (max-width: $screen-sm) { 2 | .hide-on-mobile { 3 | display: none; 4 | } 5 | } 6 | 7 | @media screen and (min-width: $screen-sm - 1) { 8 | .hide-on-desktop { 9 | display: none; 10 | } 11 | } -------------------------------------------------------------------------------- /packages/wallet-ui/src/styles/_typography.scss: -------------------------------------------------------------------------------- 1 | @mixin f-logo { 2 | font-size: $f-logo; 3 | font-weight: $f-bold; 4 | text-transform: uppercase; 5 | 6 | @media screen and (max-width: $screen-sm) { 7 | font-size: 0.8rem; 8 | } 9 | } 10 | 11 | .f-logo { 12 | @include f-logo; 13 | } 14 | 15 | @mixin f-heading { 16 | font-size: 1.7rem; 17 | font-weight: 500; 18 | } 19 | 20 | .f-heading { 21 | @include f-heading; 22 | 23 | &.centered { 24 | text-align: center; 25 | } 26 | } 27 | 28 | @mixin f-subheading { 29 | font-size: $f-heading; 30 | font-weight: $f-reg; 31 | 32 | &.centered { 33 | text-align: center; 34 | } 35 | } 36 | 37 | .f-subheading { 38 | @include f-subheading; 39 | } 40 | 41 | @mixin f-base { 42 | font-size: $f-base; 43 | font-weight: $f-reg; 44 | } 45 | 46 | .f-base { 47 | @include f-base; 48 | } 49 | -------------------------------------------------------------------------------- /packages/wallet-ui/src/styles/_variables.scss: -------------------------------------------------------------------------------- 1 | $c-black: #252525; 2 | $c-blue: #006dff; 3 | $c-bluealt: #0058e0; 4 | $c-bluelightened: #78b2ff; 5 | $c-lightgrey: #f8f8f8; 6 | $c-mediumgrey: #c6c6c6; 7 | $c-lessdarkgrey: #888888; 8 | $c-darkgrey: #707070; 9 | $c-darkergrey: #292929; 10 | $c-red: #ff0000; 11 | $c-darkred: #d02020; 12 | $c-white: #fff; 13 | 14 | $f-rem: 15px; 15 | 16 | $f-logo: 1rem; 17 | $f-heading: 1.5rem; 18 | $f-base: 1rem; 19 | $f-sm: 0.73rem; 20 | 21 | $f-light: 300; 22 | $f-reg: 400; 23 | $f-bold: 700; 24 | 25 | $site-width: 50rem; 26 | $screen-sm: 600px; 27 | 28 | $rhythm: 10px; 29 | -------------------------------------------------------------------------------- /packages/wallet-ui/src/utils/delay.ts: -------------------------------------------------------------------------------- 1 | const delay = (ms: number) => new Promise(res => setTimeout(res, ms)); 2 | 3 | export default delay; 4 | -------------------------------------------------------------------------------- /packages/wallet-ui/src/utils/log.ts: -------------------------------------------------------------------------------- 1 | export default function log(...args: any[]) { 2 | console.log("wallet-ui log:", ...args); 3 | } 4 | -------------------------------------------------------------------------------- /packages/wallet-ui/src/utils/testSelector.ts: -------------------------------------------------------------------------------- 1 | export function testSelector(name: string): string { 2 | return `[data-test-selector='${name}']`; 3 | } 4 | -------------------------------------------------------------------------------- /packages/wallet-ui/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "noEmit": true, 5 | "jsx": "preserve", 6 | "isolatedModules": true, 7 | "declaration": false, 8 | "declarationMap": false, 9 | "types": ["jest", "node", "react", "react-dom", "web3"] 10 | }, 11 | "include": ["src"] 12 | } 13 | -------------------------------------------------------------------------------- /packages/wallet-ui/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../../tslint.json"], 3 | "rules": { 4 | "variable-name": false, 5 | "ordered-imports": false 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /patches/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/counterfactual/monorepo/f431e3ded7ffeec2e1e2c3cf7b4eeb1854e1154a/patches/.gitkeep -------------------------------------------------------------------------------- /patches/ethereum-waffle+2.1.0.patch: -------------------------------------------------------------------------------- 1 | diff --git a/node_modules/ethereum-waffle/dist/compiler/saveOutput.js b/node_modules/ethereum-waffle/dist/compiler/saveOutput.js 2 | index b7b8b6c..dd0f5e9 100644 3 | --- a/node_modules/ethereum-waffle/dist/compiler/saveOutput.js 4 | +++ b/node_modules/ethereum-waffle/dist/compiler/saveOutput.js 5 | @@ -111,7 +111,7 @@ function saveOutputCombined(output, config, filesystem) { 6 | } 7 | exports.saveOutputCombined = saveOutputCombined; 8 | function getContent(contractJson, config) { 9 | - if (config.legacyOutput || !contractJson.interface) { 10 | + if (config.legacyOutput) { 11 | contractJson.interface = contractJson.abi; 12 | contractJson.bytecode = contractJson.evm.bytecode.object; 13 | } 14 | -------------------------------------------------------------------------------- /patches/scrypt+6.0.3.patch: -------------------------------------------------------------------------------- 1 | diff --git a/node_modules/scrypt/index.js b/node_modules/scrypt/index.js 2 | index 7b13a79..1e40d76 100644 3 | --- a/node_modules/scrypt/index.js 4 | +++ b/node_modules/scrypt/index.js 5 | @@ -1,6 +1,6 @@ 6 | "use strict"; 7 | 8 | -var scryptNative = require("./build/Release/scrypt") 9 | +var scryptNative = require("./build/Release/scrypt.node") 10 | , Crypto = require("crypto") 11 | , Os = require("os"); 12 | 13 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["config:base"], 3 | "schedule": ["every weekend"], 4 | "baseBranches": ["next"], 5 | "automerge": true, 6 | "ignoreDeps": [ 7 | "firebase", 8 | "@stencil/core", 9 | "@stencil/router", 10 | "@stencil/sass", 11 | "@types/jest", 12 | "jest-cli", 13 | "jest", 14 | "react-scripts", 15 | "workbox-build", 16 | "@ebryn/jsonapi-ts", 17 | "@counterfactual/apps", 18 | "@counterfactual/cf.js", 19 | "@counterfactual/cf-adjudicator-contracts", 20 | "@counterfactual/cf-funding-protocol-contracts", 21 | "@counterfactual/node-provider", 22 | "@counterfactual/node", 23 | "@counterfactual/specs", 24 | "@counterfactual/types", 25 | "@counterfactual/typescript-typings" 26 | ] 27 | } 28 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowJs": false, 4 | "emitDecoratorMetadata": true, 5 | "experimentalDecorators": true, 6 | "noImplicitAny": false, 7 | "noUnusedParameters": false, 8 | "noUnusedLocals": false, 9 | "noImplicitReturns": true, 10 | "noLib": false, 11 | "removeComments": true, 12 | "declaration": true, 13 | "declarationMap": true, 14 | "forceConsistentCasingInFileNames": true, 15 | "moduleResolution": "node", 16 | "resolveJsonModule": true, 17 | "allowSyntheticDefaultImports": true, 18 | "esModuleInterop": true, 19 | "lib": ["es2017", "dom", "esnext.asynciterable"], 20 | "target": "es2017", 21 | "module": "esnext", 22 | "sourceMap": true, 23 | "typeRoots": [ 24 | "./node_modules/@types", 25 | "./node_modules/@counterfactual/typescript-typings/types" 26 | ], 27 | // @types/mocha has a duplicate conflict, this is the suggested temp fix. 28 | "skipLibCheck": true, 29 | "strict": true 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "tslint-config-airbnb", 4 | "tslint-plugin-prettier", 5 | "tslint-config-prettier" 6 | ], 7 | "rules": { 8 | "prettier": true, 9 | // NOTE: Added by Liam. I just prefer this rule to be on. Looks clean. 10 | "ordered-imports": [true, { "grouped-imports": true }], 11 | // See https://github.com/counterfactual/monorepo/pull/231#discussion_r233860710 12 | "import-name": false, 13 | // NOTE: Added by Liam. Preference. 14 | "prefer-readonly": true, 15 | // NOTE: Added by Liam, another preference. 16 | "prefer-conditional-expression": true, 17 | "prefer-array-literal": false 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /waffle.js: -------------------------------------------------------------------------------- 1 | require("dotenv").config(); 2 | 3 | var waffleConfig = { 4 | "npmPath": "../../node_modules", 5 | "legacyOutput": false, 6 | "compilerOptions": { 7 | "optimizer": { 8 | "enabled": true, 9 | "runs": 200 10 | }, 11 | "evmVersion": "petersburg", 12 | "metadata": { 13 | "useLiteralContent": true 14 | }, 15 | "outputSelection": { 16 | "*": { 17 | "*": [ 18 | "evm.bytecode.object", "abi", /* "metadata" */ 19 | ], 20 | }, 21 | } 22 | } 23 | }; 24 | 25 | var selectSolc = () => { 26 | // TODO: which should select "native" in CI, but the solc binary in the CI 27 | // environment is currently too old 28 | if (process.env.NATIVE_SOLC == "true") { 29 | waffleConfig.compiler = "native"; 30 | } 31 | 32 | return waffleConfig; 33 | } 34 | 35 | module.exports = selectSolc; 36 | --------------------------------------------------------------------------------