├── .eslintignore ├── .eslintrc ├── .github ├── dependabot.yaml └── workflows │ ├── ci.yaml │ ├── close-stale-issue-and-pr.yaml │ ├── dependency-review.yaml │ ├── docker-build.yaml │ └── markdown-link-check.yaml ├── .gitignore ├── .npmrc ├── .nvmrc ├── .vscode ├── launch.json └── settings.json ├── Makefile ├── README.md ├── hardhat.config.js ├── logs └── polygon_contract_deployment_log.txt ├── matchstick.yaml ├── package-lock.json ├── package.json ├── packages ├── chat-contracts │ ├── .gitignore │ ├── .solcover.js │ ├── .solhint.json │ ├── README.md │ ├── contracts │ │ ├── DelegatedAccessRegistry.sol │ │ ├── Factories │ │ │ ├── ERC1155PolicyFactory.sol │ │ │ ├── ERC20PolicyFactory.sol │ │ │ ├── ERC721PolicyFactory.sol │ │ │ ├── ERC777PolicyFactory.sol │ │ │ └── TokenGateFactory.sol │ │ ├── JoinPolicies │ │ │ ├── ERC1155JoinPolicy.sol │ │ │ ├── ERC20JoinPolicy.sol │ │ │ ├── ERC721JoinPolicy.sol │ │ │ ├── ERC777JoinPolicy.sol │ │ │ └── JoinPolicy.sol │ │ ├── JoinPolicyRegistry.sol │ │ └── TestTokens │ │ │ ├── ERC1155TestToken.sol │ │ │ ├── ERC20TestToken.sol │ │ │ ├── ERC721TestToken.sol │ │ │ └── ERC777TestToken.sol │ ├── deployedAddresses.json │ ├── hardhat.config.ts │ ├── package-lock.json │ ├── package.json │ ├── scripts │ │ ├── .eslintrc.json │ │ ├── 1_deployDelegatedAccessRegistry.ts │ │ ├── 2_deployJoinPolicyRegistry.ts │ │ └── 3_deployTokenGateFactories.ts │ ├── test │ │ ├── DelegatedAccessRegistry.test.ts │ │ ├── ERC1155JoinPolicy.test.ts │ │ ├── ERC20JoinPolicy.test.ts │ │ ├── ERC721JoinPolicy.test.ts │ │ ├── ERC777JoinPolicy.test.ts │ │ ├── JoinPolicyRegistry.test.ts │ │ └── TokenGatedFactories.test.ts │ └── tsconfig.json ├── config │ ├── .gitignore │ ├── LICENSE │ ├── Makefile │ ├── README.md │ ├── config.json │ ├── docs │ │ └── draft-release-github.png │ ├── mocharc.json │ ├── package.json │ ├── release-git-tag.bash │ ├── release-git-validate.bash │ ├── release-npm-update-version-package-json.bash │ ├── release-validate-semver.bash │ ├── release.bash │ ├── scripts │ │ └── generate-types.mjs │ ├── src │ │ └── index.ts │ ├── test │ │ └── index.test.ts │ ├── tsconfig-test.json │ └── tsconfig.json ├── dev-chain-fast │ ├── Dockerfile │ ├── README.md │ ├── hardhat.config.js │ ├── package-lock.json │ ├── package.json │ ├── prepare.sh │ ├── scripts │ │ └── checkTimestamp.ts │ ├── src │ │ ├── deploy.ts │ │ └── projects.ts │ ├── start.sh │ ├── tsconfig.build.json │ └── tsconfig.json ├── ens-sync-script │ ├── .gitignore │ ├── Dockerfile │ ├── README.md │ ├── healthcheck.sh │ ├── package.json │ ├── prepare.sh │ ├── src │ │ ├── eth-ens-namehash.d.ts │ │ └── index.ts │ └── tsconfig.json ├── hub-contracts │ ├── .gitignore │ ├── .mocharc.json │ ├── .openzeppelin │ │ ├── polygon.json │ │ ├── unknown-100.json │ │ ├── unknown-3338.json │ │ └── unknown-80002.json │ ├── .prettierrc │ ├── .solcover.js │ ├── .solhint.json │ ├── .vscode │ │ └── launch.json │ ├── README.md │ ├── archive │ │ └── MarketplaceV3_1.sol │ ├── contracts │ │ ├── Marketplace │ │ │ ├── IInterchainGasPaymaster.sol │ │ │ ├── IInterchainQueryRouter.sol │ │ │ ├── IMailbox.sol │ │ │ ├── IMarketplace.sol │ │ │ ├── IMarketplaceV4.sol │ │ │ ├── IMessageRecipient.sol │ │ │ ├── IPurchaseListener.sol │ │ │ ├── IRemoteMarketplaceV1.sol │ │ │ ├── MarketplaceV3.sol │ │ │ ├── MarketplaceV4.sol │ │ │ ├── RemoteMarketplaceV1.sol │ │ │ ├── Uniswap2Adapter.sol │ │ │ └── Uniswap2AdapterV4.sol │ │ ├── ProjectRegistry │ │ │ ├── IProjectRegistryV1.sol │ │ │ └── ProjectRegistryV1.sol │ │ ├── ProjectStaking │ │ │ ├── IProjectStakingV1.sol │ │ │ └── ProjectStakingV1.sol │ │ ├── chainlinkClient │ │ │ └── ENSCache.sol │ │ ├── test-contracts │ │ │ ├── ERC20Mintable.sol │ │ │ ├── GasReporter.sol │ │ │ └── MockMarketplaceBeneficiary.sol │ │ └── token │ │ │ ├── DATAv2.sol │ │ │ ├── IERC677.sol │ │ │ └── IERC677Receiver.sol │ ├── hardhat.config.ts │ ├── package.json │ ├── scripts │ │ ├── deployMarketplaceV3.ts │ │ ├── deployMarketplaceV4.ts │ │ ├── deployProjectRegistry.ts │ │ ├── deployProjectStakingV1.ts │ │ ├── deployRemoteMarketplace.ts │ │ ├── deployUniswapAdapter.ts │ │ ├── hubEnvDeployer.ts │ │ ├── interactMarketplaceV3.ts │ │ ├── interactMarketplaceV4.ts │ │ ├── interactProjectRegistry.ts │ │ ├── interactProjectStaking.ts │ │ ├── interactRemoteMarketplace.ts │ │ ├── interactStreamRegistry.ts │ │ ├── upgradeMarketplaceV3.ts │ │ ├── upgradeProjectRegistry.ts │ │ ├── upgradeProjectStaking.ts │ │ └── upgradeRemoteMarketplace.ts │ ├── src │ │ ├── HubEnvDeployer.ts │ │ ├── contracts │ │ │ └── enums.ts │ │ ├── exports.ts │ │ └── startWatcher.ts │ ├── test-contracts │ │ └── TestToken.json │ ├── test │ │ ├── .eslintrc.js │ │ ├── constants.ts │ │ └── contracts │ │ │ ├── MarketplaceV3.test.ts │ │ │ ├── MarketplaceV4.test.ts │ │ │ ├── ProjectRegistry.test.ts │ │ │ ├── ProjectStakingV1.test.ts │ │ │ └── Uniswap2Adapter.test.ts │ ├── tsconfig.build.json │ ├── tsconfig.json │ └── utils.ts ├── network-contracts │ ├── .eslintignore │ ├── .eslintrc.json │ ├── .gitignore │ ├── .openzeppelin │ │ ├── polygon.json │ │ ├── unknown-3338.json │ │ ├── unknown-4689.json │ │ ├── unknown-4690.json │ │ └── unknown-80002.json │ ├── .solhint.json │ ├── .vscode │ │ ├── launch.json │ │ └── settings.json │ ├── README.md │ ├── build.sh │ ├── contracts │ │ ├── GSN │ │ │ ├── AcceptEverythingPaymaster.sol │ │ │ └── WhitelistPaymaster.sol │ │ ├── NodeRegistry │ │ │ ├── ERC20Mintable.sol │ │ │ ├── NetworkParameters.sol │ │ │ ├── NodeDomainNameHelper.sol │ │ │ ├── NodeRegistry.sol │ │ │ ├── Ownable.sol │ │ │ ├── TokenBalanceWeightStrategy.sol │ │ │ ├── TrackerRegistry.sol │ │ │ ├── WeightStrategy.sol │ │ │ └── WeightedNodeRegistry.sol │ │ ├── OperatorTokenomics │ │ │ ├── IERC677.sol │ │ │ ├── IERC677Receiver.sol │ │ │ ├── IOperator.sol │ │ │ ├── IRandomOracle.sol │ │ │ ├── IVoterRegistry.sol │ │ │ ├── Operator.sol │ │ │ ├── OperatorFactory.sol │ │ │ ├── OperatorPolicies │ │ │ │ ├── DefaultDelegationPolicy.sol │ │ │ │ ├── DefaultExchangeRatePolicy.sol │ │ │ │ ├── DefaultUndelegationPolicy.sol │ │ │ │ ├── IDelegationPolicy.sol │ │ │ │ ├── IExchangeRatePolicy.sol │ │ │ │ ├── INodeModule.sol │ │ │ │ ├── IQueueModule.sol │ │ │ │ ├── IStakeModule.sol │ │ │ │ ├── IUndelegationPolicy.sol │ │ │ │ ├── NodeModule.sol │ │ │ │ ├── QueueModule.sol │ │ │ │ └── StakeModule.sol │ │ │ ├── Sponsorship.sol │ │ │ ├── SponsorshipFactory.sol │ │ │ ├── SponsorshipPolicies │ │ │ │ ├── AdminKickPolicy.sol │ │ │ │ ├── DefaultLeavePolicy.sol │ │ │ │ ├── IAllocationPolicy.sol │ │ │ │ ├── IJoinPolicy.sol │ │ │ │ ├── IKickPolicy.sol │ │ │ │ ├── ILeavePolicy.sol │ │ │ │ ├── MaxOperatorsJoinPolicy.sol │ │ │ │ ├── OperatorContractOnlyJoinPolicy.sol │ │ │ │ ├── StakeWeightedAllocationPolicy.sol │ │ │ │ └── VoteKickPolicy.sol │ │ │ ├── StreamrConfig.sol │ │ │ ├── StreamrConfigV1_1.sol │ │ │ └── testcontracts │ │ │ │ ├── MockRandomOracle.sol │ │ │ │ ├── TestAllocationPolicy.sol │ │ │ │ ├── TestBadOperator.sol │ │ │ │ ├── TestExchangeRatePolicy.sol │ │ │ │ ├── TestExchangeRatePolicy2.sol │ │ │ │ ├── TestExchangeRatePolicy3.sol │ │ │ │ ├── TestJoinPolicy.sol │ │ │ │ ├── TestKickPolicy.sol │ │ │ │ └── TestToken.sol │ │ ├── StreamRegistry │ │ │ ├── ERC2771ContextUpgradeable.sol │ │ │ ├── IStreamRegistryV4.sol │ │ │ ├── StreamRegistry.sol │ │ │ ├── StreamRegistryV2.sol │ │ │ ├── StreamRegistryV3.sol │ │ │ ├── StreamRegistryV4.sol │ │ │ ├── StreamRegistryV4_1.sol │ │ │ ├── StreamRegistryV5.sol │ │ │ └── StreamRegistryV5_1.sol │ │ ├── StreamStorageRegistry │ │ │ ├── StreamStorageRegistry.sol │ │ │ └── StreamStorageRegistryV2.sol │ │ └── chainlinkClient │ │ │ ├── ENSCache.sol │ │ │ ├── ENSCacheV1.sol │ │ │ └── ENSCacheV2Streamr.sol │ ├── deployedAddresses.json │ ├── doc │ │ └── peaq_deployment.md │ ├── generateSelectorsTxt.sh │ ├── hardhat.config.ts │ ├── logs │ │ ├── 2024-02-20-undelegation-limit-deployment.txt │ │ ├── 2024-02-21-operator-template-v1.txt │ │ ├── 2024-02-22-sip-20.txt │ │ ├── 2024-03-06-operator-template-v2.txt │ │ ├── 2024-03-07-temp-low-minimumstake.txt │ │ ├── 2024-03-12-revert-minimumstake.txt │ │ ├── 2024-03-15-operator-template-v3.txt │ │ ├── 2024-10-30-deploy-to-iotex-testnet.txt │ │ ├── 2024-11-14-streamregistry-v5.txt │ │ ├── 2024-12-13-deploy-to-iotex.txt │ │ ├── 2025-05-15-deploy-to-iotex.txt │ │ └── 2025-05-30-upgrade-peaq-streamregistry.txt │ ├── package.json │ ├── polygonDateToBlockNumberCache.json │ ├── scripts │ │ ├── .eslintrc.json │ │ ├── 2023-deployToMumbai.sh │ │ ├── 2023-deployToPolygon.sh │ │ ├── 2024-02-20-undelegation-limit-deployment.sh │ │ ├── 2024-02-21-operator-template-redeployment.sh │ │ ├── 2024-02-22-sip-20.sh │ │ ├── 2024-03-06-operator-template-v2.sh │ │ ├── 2024-03-07-temp-low-minimumstake.sh │ │ ├── 2024-03-12-revert-minimumstake.sh │ │ ├── 2024-03-15-operator-template-v3.sh │ │ ├── 2024-05-03-deployToAmoy.sh │ │ ├── 2024-05-22-eth-677-reviewer-selection.sh │ │ ├── 2024-05-27-streamregistry-v5-amoy.sh │ │ ├── 2024-10-22-streamregistry-v5-amoy.sh │ │ ├── 2024-10-30-deploy-to-iotex-testnet.sh │ │ ├── 2024-10-30-deploy-to-iotex.sh │ │ ├── 2024-11-14-streamregistry-v5.sh │ │ ├── 2024-12-04-deploy-to-iotex.sh │ │ ├── 2024-12-13-deploy-to-iotex.sh │ │ ├── 2025-05-15-deploy-to-iotex.sh │ │ ├── _read.ts │ │ ├── analyzeProtocolIncomes.ts │ │ ├── analyzeSlashings.ts │ │ ├── calculateFullReviewProbability.ts │ │ ├── check.ts │ │ ├── checkers │ │ │ └── check-StreamRegistry.ts │ │ ├── createStream.ts │ │ ├── data │ │ │ ├── 2023-12-22_and_before │ │ │ │ ├── 10-data-or-more.csv │ │ │ │ ├── below-10-data-summed.csv │ │ │ │ ├── slashings.csv │ │ │ │ └── slashings.csv.short.csv │ │ │ ├── 2024-04-16 │ │ │ │ ├── slashings.csv │ │ │ │ ├── slashings.csv.short.csv │ │ │ │ └── slashings.csv.transfers.csv │ │ │ ├── 2024-08-15-0x91 │ │ │ │ ├── slashings.csv │ │ │ │ ├── slashings.csv.short.csv │ │ │ │ └── slashings.csv.transfers.csv │ │ │ ├── 2024-08-16 │ │ │ │ ├── slashings.csv │ │ │ │ ├── slashings.csv.short.csv │ │ │ │ └── slashings.csv.transfers.csv │ │ │ ├── DATAv2.json │ │ │ ├── protocol-fee-transfers-analyzed.csv │ │ │ └── protocol-fee-transfers.csv │ │ ├── delegate.ts │ │ ├── deployGSN.ts │ │ ├── deployStreamrContracts.ts │ │ ├── deployToLivenet │ │ │ ├── 1_deployContracts.ts │ │ │ ├── 2_interactWithContracts.ts │ │ │ ├── 3_deployDelegatedAccessRegistry.ts │ │ │ ├── 4_deployEnsCacheScript.ts │ │ │ ├── 4a_prod_deployEnsCacheScript.ts │ │ │ ├── upgradeStreamRegistry.ts │ │ │ └── upgradeStreamStorageRegistry.ts │ │ ├── deployTokenomicsContracts.ts │ │ ├── handOverAdminRolesTo.ts │ │ ├── modifyENSCache.ts │ │ ├── prettyPrint.ts │ │ ├── registerENSName.ts │ │ ├── registerStorageNode.ts │ │ ├── removeStorageNode.ts │ │ ├── setTrusted.ts │ │ ├── streamrConfig.ts │ │ ├── tatum │ │ │ ├── 1_deploySponsorshipFactory.ts │ │ │ ├── 2_deployNewSponsorship.ts │ │ │ ├── 3_deployOperatorFactory.ts │ │ │ ├── 4_deployNewOperator.ts │ │ │ ├── StreamrEnvDeployerHardhat.ts │ │ │ ├── streamrEnvDeployer.ts │ │ │ └── transactThroughGSN.ts │ │ ├── unstake.ts │ │ ├── upgradeOperatorTemplate.ts │ │ ├── upgradeStreamRegistry.ts │ │ ├── upgradeStreamrConfig.ts │ │ ├── upgradeUndelegationPolicy.ts │ │ ├── upgradeVoteKickPolicy.ts │ │ └── utils │ │ │ ├── bigint.ts │ │ │ ├── dateToBlockNumber.ts │ │ │ ├── dateToBlockNumberPolygonApi.ts │ │ │ ├── dateToBlockNumberPolygonSubgraph.ts │ │ │ └── sleep.ts │ ├── selectors.txt │ ├── src │ │ ├── StreamrEnvDeployer.ts │ │ └── exports.ts │ ├── tasks │ │ └── copyFilesAfterCompilation.ts │ ├── test │ │ ├── hardhat-slow-tests │ │ │ └── Operator.test.ts │ │ ├── hardhat │ │ │ ├── OperatorTokenomics │ │ │ │ ├── Operator.test.ts │ │ │ │ ├── OperatorFactory.test.ts │ │ │ │ ├── Sponsorship.test.ts │ │ │ │ ├── SponsorshipFactory.test.ts │ │ │ │ ├── SponsorshipPolicies │ │ │ │ │ ├── AdminKickPolicy.test.ts │ │ │ │ │ ├── DefaultLeavePolicy.test.ts │ │ │ │ │ ├── MaxOperatorsJoinPolicy.test.ts │ │ │ │ │ ├── OperatorContractOnlyJoinPolicy.test.ts │ │ │ │ │ ├── StakeWeightedAllocationPolicy.test.ts │ │ │ │ │ └── VoteKickPolicy.test.ts │ │ │ │ ├── StreamrConfig.test.ts │ │ │ │ ├── deployOperatorContract.ts │ │ │ │ ├── deploySponsorshipContract.ts │ │ │ │ ├── deployTestContracts.ts │ │ │ │ ├── setupSponsorships.ts │ │ │ │ └── utils.ts │ │ │ └── Registries │ │ │ │ ├── ENScache.test.ts │ │ │ │ ├── NodeRegistry.test.ts │ │ │ │ ├── StreamRegistry.test.ts │ │ │ │ ├── StreamStorageRegistry.test.ts │ │ │ │ └── getEIP2771MetaTx.ts │ │ └── integration │ │ │ ├── contractsSubgraphSmoketest.test.ts │ │ │ └── ens-e2e.test.ts │ ├── tsconfig.build.json │ └── tsconfig.json ├── network-subgraphs │ ├── .gitignore │ ├── Dockerfile │ ├── Dockerfile.matchstick │ ├── README.md │ ├── abis │ │ ├── NodeRegistry.json │ │ ├── StreamRegistry.json │ │ └── StreamStorageRegistry.json │ ├── package.json │ ├── schema.graphql │ ├── scripts │ │ ├── copyAbisFromContractsPackage.sh │ │ ├── deployRetry.sh │ │ └── generateConfigKeys.ts │ ├── src │ │ ├── .eslintrc.json │ │ ├── helpers.ts │ │ ├── marketplaceV4.ts │ │ ├── nodeRegistry.ts │ │ ├── operator.ts │ │ ├── operatorFactory.ts │ │ ├── projectRegistry.ts │ │ ├── projectStaking.ts │ │ ├── sponsorship.ts │ │ ├── sponsorshipFactory.ts │ │ ├── streamRegistry.ts │ │ ├── streamStorageRegistry.ts │ │ └── streamrConfig.ts │ ├── start.sh │ ├── subgraph.template.yaml │ ├── subgraph.yaml │ ├── subgraph_amoy.yaml │ ├── subgraph_iotex.yaml │ ├── subgraph_peaq.yaml │ ├── subgraph_polygon.yaml │ └── tests │ │ ├── .bin │ │ └── .gitignore │ │ ├── helpers │ │ ├── mocked-entity.ts │ │ └── mocked-event.ts │ │ ├── integration │ │ └── event-queue-test.js │ │ ├── marketplaceV4.test.ts │ │ ├── projectRegistry.test.ts │ │ └── projectStakingV1.test.ts └── npm-network-contracts │ ├── .gitignore │ ├── README.md │ ├── build.sh │ ├── hardhat.config.ts │ ├── minify-abi.mjs │ ├── package-lock.json │ ├── package.json │ ├── src │ └── exports.ts │ └── tsconfig.json └── scripts ├── copyAbis.sh └── generateYaml.sh /.eslintignore: -------------------------------------------------------------------------------- 1 | packages/tatum-subgraph/generated/* 2 | packages/network-subgraphs/generated/* 3 | **/typechain 4 | **/build 5 | **/dist -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "eslint-config-streamr-ts", 3 | "plugins": [ 4 | "chai-friendly" 5 | ], 6 | "parserOptions": { 7 | "ecmaVersion": 2021 8 | }, 9 | "env": { 10 | "node": true, 11 | "mocha": true 12 | }, 13 | "globals": { 14 | // mocha 15 | "describe": "readonly", 16 | "it": "readonly", 17 | "before": "readonly", 18 | "beforeEach": "readonly", 19 | "after": "readonly", 20 | "afterEach": "readonly" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /.github/dependabot.yaml: -------------------------------------------------------------------------------- 1 | # 2 | # https://docs.github.com/en/free-pro-team@latest/github/administering-a-repository/configuration-options-for-dependency-updates 3 | # https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file#docker 4 | # 5 | --- 6 | version: 2 7 | updates: 8 | - package-ecosystem: github-actions 9 | directory: / 10 | schedule: 11 | interval: daily 12 | time: '08:00' 13 | timezone: Europe/Helsinki 14 | open-pull-requests-limit: 5 15 | commit-message: 16 | prefix: ci 17 | labels: 18 | - ci 19 | assignees: 20 | - DaisyDomergue 21 | 22 | - package-ecosystem: npm 23 | directory: /packages/config 24 | schedule: 25 | interval: daily 26 | time: '08:00' 27 | timezone: Europe/Helsinki 28 | open-pull-requests-limit: 2 29 | commit-message: 30 | prefix: build 31 | include: scope 32 | labels: 33 | - build 34 | 35 | - package-ecosystem: npm 36 | directory: /packages/network-contracts 37 | schedule: 38 | interval: daily 39 | time: '08:00' 40 | timezone: Europe/Helsinki 41 | open-pull-requests-limit: 2 42 | commit-message: 43 | prefix: build 44 | include: scope 45 | labels: 46 | - build 47 | 48 | - package-ecosystem: npm 49 | directory: /packages/network-subgraphs 50 | schedule: 51 | interval: daily 52 | time: '08:00' 53 | timezone: Europe/Helsinki 54 | open-pull-requests-limit: 2 55 | commit-message: 56 | prefix: build 57 | include: scope 58 | labels: 59 | - build 60 | 61 | - package-ecosystem: docker 62 | directory: /packages/network-subgraphs 63 | schedule: 64 | interval: daily 65 | time: "08:00" 66 | timezone: Europe/Helsinki 67 | open-pull-requests-limit: 5 68 | commit-message: 69 | prefix: build(docker) 70 | labels: 71 | - build 72 | 73 | - package-ecosystem: docker 74 | directory: ./packages/dev-chain-fast 75 | schedule: 76 | interval: daily 77 | time: "08:00" 78 | timezone: Europe/Helsinki 79 | open-pull-requests-limit: 5 80 | commit-message: 81 | prefix: build(docker) 82 | labels: 83 | - build 84 | -------------------------------------------------------------------------------- /.github/workflows/close-stale-issue-and-pr.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Close stale issues and PRs CI 3 | 4 | on: 5 | schedule: 6 | - cron: "0 0 * * *" 7 | 8 | # https://github.com/marketplace/actions/close-stale-issues 9 | 10 | jobs: 11 | stale: 12 | name: Close stale issues and PRs 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/stale@v9 16 | with: 17 | repo-token: ${{ secrets.GITHUB_TOKEN }} 18 | days-before-stale: 60 19 | days-before-close: 7 20 | stale-issue-message: 'This issue is stale because it has been open 60 days with no activity. Remove no-issue-activity label or comment or this will be closed in 7 days' 21 | close-issue-message: 'This issue is closed due to inactivity.' 22 | stale-issue-label: 'no-issue-activity' 23 | stale-pr-message: 'This pull request is stale because it has been open 60 days with no activity. Remove no-pr-activity label or comment or this will be closed in 7 days' 24 | close-pr-message: 'This pull request is closed due to inactivity.' 25 | stale-pr-label: 'no-pr-activity' 26 | -------------------------------------------------------------------------------- /.github/workflows/dependency-review.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Dependency 3 | # on: 4 | # - pull_request 5 | 6 | # TODO: temporarily disable the dependency review, please put back on before 1.0 7 | on: 8 | pull_request: 9 | branches: 10 | - lol 11 | 12 | permissions: 13 | contents: read 14 | 15 | jobs: 16 | dependency-review: 17 | name: Review 18 | runs-on: ubuntu-latest 19 | steps: 20 | - uses: actions/checkout@v3 21 | - name: Dependency Review 22 | uses: actions/dependency-review-action@v4 23 | -------------------------------------------------------------------------------- /.github/workflows/markdown-link-check.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Markdown CI 3 | 4 | # https://github.com/gaurav-nelson/github-action-markdown-link-check 5 | 6 | on: 7 | schedule: 8 | - cron: "0 5 * * 1" 9 | 10 | jobs: 11 | check-links: 12 | name: Check Markdown links 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v3 16 | - uses: gaurav-nelson/github-action-markdown-link-check@1.0.15 17 | with: 18 | use-quiet-mode: 'yes' 19 | use-verbose-mode: 'yes' 20 | max-depth: 1 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **node_modules/ 2 | **build 3 | **cache 4 | /packages/tatum-thegraph-subgraph/artifacts 5 | /packages/tatum-thegraph-subgraph/cache 6 | /packages/tatum-thegraph-subgraph/generated 7 | /bin/ 8 | .DS_Store 9 | .idea 10 | unknown-31337.json 11 | dist 12 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | save-exact=true 2 | save-prefix="" 3 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 18.20.5 -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "solidity.defaultCompiler": "remote" 3 | } -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | LANG := en_US.UTF-8 2 | SHELL := /bin/bash 3 | .SHELLFLAGS := --norc --noprofile -e -u -o pipefail -c 4 | .DEFAULT_GOAL := help 5 | 6 | nvm_brew = /usr/local/opt/nvm/nvm.sh 7 | ifneq ("$(wildcard $(nvm_brew))", "") 8 | nvm_sh = $(nvm_brew) 9 | endif 10 | nvm_default = $(HOME)/.nvm/nvm.sh 11 | ifneq ("$(wildcard $(nvm_default))", "") 12 | nvm_sh = $(nvm_default) 13 | endif 14 | define npm 15 | @$(eval npm_args=$(1)) 16 | bash --norc --noprofile -e -o pipefail -l -c "source $(nvm_sh) && nvm exec npm $(npm_args)" 17 | endef 18 | export NODE_ENV := "development" 19 | 20 | .PHONY: npm-install 21 | npm-install: ## Run 'npm install' 22 | $(call npm, install) 23 | 24 | .PHONY: clean 25 | clean: ## Remove generated files 26 | $(RM) -r \ 27 | node_modules 28 | 29 | .PHONY: help 30 | help: ## Show Help 31 | @grep -E '^[a-zA-Z0-9_\-\/]+%?:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "%-20s %s\n", $$1, $$2}' | sort 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This is a monorepo containing various smart contracts and subgraphs used by the Streamr Network, as well as potential other Ethereum-related bits and pieces that relate to the Network. 2 | 3 | ## Main packages 4 | 5 | - [Network contracts](https://github.com/streamr-dev/network-contracts/tree/master/packages/network-contracts) The actual smart contracts used by the Streamr Network. Package exported from here has version 7.x.x and exports Typechain interfaces for Ethers v5. 6 | - [Network contracts NPM package](https://github.com/streamr-dev/network-contracts/tree/master/packages/npm-network-contracts) ABI and Typechain interfaces for the Streamr Network smart contracts for Ethers v6, plus scripts for interacting with the smart contracts. 7 | - [Network subgraphs](https://github.com/streamr-dev/network-contracts/tree/master/packages/network-subgraphs) The Graph subgraphs for many contracts in the `network-contracts` package 8 | - [config](https://github.com/streamr-dev/network-contracts/tree/master/packages/config) Addresses of deployed Streamr contracts on various chains, importable as an npm package -------------------------------------------------------------------------------- /matchstick.yaml: -------------------------------------------------------------------------------- 1 | testsFolder: ./packages/network-subgraphs/tests 2 | manifestPath: ./packages/network-subgraphs/subgraph.yaml 3 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "streamr-contracts", 3 | "version": "0.0.0", 4 | "description": "Network Contracts with The Graph", 5 | "author": "Streamr Network AG ", 6 | "license": "STREAMR NETWORK OPEN SOURCE LICENSE", 7 | "private": true, 8 | "workspaces": [ 9 | "./packages/config", 10 | "./packages/network-contracts", 11 | "./packages/hub-contracts", 12 | "./packages/network-subgraphs", 13 | "./packages/chat-contracts", 14 | "./packages/ens-sync-script", 15 | "./packages/dev-chain-fast" 16 | ], 17 | "scripts": { 18 | "build": "npm run build --workspaces --if-present", 19 | "clean": "npm run clean --workspaces --if-present", 20 | "lint": "npm run lint --workspaces --if-present && eslint .", 21 | "test": "npm run test --workspaces --if-present", 22 | "integration-test": "npm run integration-test --workspaces --if-present", 23 | "test:subgraph": "graph test -d", 24 | "graph1_build": "npm run deployLocalWithExport -w=network-contracts", 25 | "graph2_copyInfo": "./scripts/copyAbis.sh && ./scripts/generateYaml.sh", 26 | "graph3_build": "npm run doAll --workspace=network-subgraphs", 27 | "graph": "npm run graph1_build && npm run graph2_copyInfo && npm run graph3_build", 28 | "e2etest": "npm run e2etest --workspace=network-contracts" 29 | }, 30 | "devDependencies": { 31 | "@chainsafe/ssz": "^0.10.2", 32 | "@types/chai": "4.3.0", 33 | "@types/mocha": "9.1.1", 34 | "@types/node": "16.11.25", 35 | "@typescript-eslint/eslint-plugin": "5.42.1", 36 | "@typescript-eslint/parser": "^5.42.1", 37 | "chai": "4.3.6", 38 | "eslint": "8.27.0", 39 | "eslint-config-streamr-ts": "4.1.0", 40 | "eslint-plugin-chai-friendly": "0.7.2", 41 | "eslint-plugin-promise": "6.1.1", 42 | "gluegun": "5.1.2", 43 | "keyv": "4.5.2", 44 | "mocha": "9.2.0", 45 | "ts-node": "10.4.0", 46 | "typescript": "4.2.4" 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /packages/chat-contracts/.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules/ 2 | /coverage/ 3 | /coverage.json 4 | /artifacts/ 5 | /deployments/ 6 | /typechain/ 7 | /cache/ 8 | .env -------------------------------------------------------------------------------- /packages/chat-contracts/.solcover.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | skipFiles: ["JoinPolicies/JoinPolicy.sol"] 3 | } -------------------------------------------------------------------------------- /packages/chat-contracts/.solhint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "solhint:recommended", 3 | "plugins": [], 4 | "rules": { 5 | "code-complexity": [ 6 | "error", 7 | 7 8 | ], 9 | "function-max-lines": [ 10 | "error", 11 | 35 12 | ], 13 | "max-states-count": [ 14 | "error", 15 | 15 16 | ], 17 | "no-empty-blocks": "off", 18 | "no-unused-vars": "error", 19 | "payable-fallback": "error", 20 | "reason-string": [ 21 | "error", 22 | { 23 | "maxLength": 32 24 | } 25 | ], 26 | "constructor-syntax": "error", 27 | "const-name-snakecase": "error", 28 | "event-name-camelcase": "error", 29 | "func-name-mixedcase": "error", 30 | "func-param-name-mixedcase": "error", 31 | "modifier-name-mixedcase": "error", 32 | "use-forbidden-name": "error", 33 | "var-name-mixedcase": "error", 34 | "imports-on-top": "error", 35 | "visibility-modifier-order": "error", 36 | "avoid-call-value": "error", 37 | "avoid-low-level-calls": "error", 38 | "avoid-sha3": "error", 39 | "avoid-suicide": "error", 40 | "avoid-throw": "error", 41 | "avoid-tx-origin": "error", 42 | "check-send-result": "error", 43 | "multiple-sends": "error", 44 | "no-complex-fallback": "error", 45 | "no-inline-assembly": "warn", 46 | "not-rely-on-block-hash": "error", 47 | "not-rely-on-time": "warn", 48 | "reentrancy": "error", 49 | "state-visibility": "error", 50 | "func-visibility": [ 51 | "error", 52 | { 53 | "ignoreConstructors": true 54 | } 55 | ], 56 | "quotes": [ 57 | "error", 58 | "double" 59 | ], 60 | "max-line-length": [ 61 | "error", 62 | 200 63 | ], 64 | "compiler-version": [ 65 | "error", 66 | ">=0.6.0" 67 | ] 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /packages/chat-contracts/README.md: -------------------------------------------------------------------------------- 1 | # thechat.eth smart contracts -------------------------------------------------------------------------------- /packages/chat-contracts/contracts/Factories/ERC1155PolicyFactory.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Unlicense 2 | pragma solidity ^0.8.9; 3 | 4 | import "./TokenGateFactory.sol"; 5 | import "../JoinPolicies/ERC1155JoinPolicy.sol"; 6 | 7 | contract ERC1155PolicyFactory is TokenGateFactory{ 8 | 9 | constructor( 10 | address joinPolicyRegistryAddress_, 11 | address streamRegistryAddress_, 12 | address delegatedAccessRegistryAddress_ 13 | ) TokenGateFactory( 14 | joinPolicyRegistryAddress_, 15 | streamRegistryAddress_, 16 | delegatedAccessRegistryAddress_ 17 | ){} 18 | 19 | function create( 20 | address tokenAddress, 21 | string memory streamId_, 22 | uint256 minRequiredBalance_, 23 | uint256 tokenId_, 24 | bool stakingEnabled_, 25 | StreamRegistryV3.PermissionType[] memory defaultPermissions_ 26 | ) public override { 27 | ERC1155JoinPolicy instance = new ERC1155JoinPolicy( 28 | tokenAddress, 29 | streamRegistryAddress, 30 | streamId_, 31 | defaultPermissions_, 32 | tokenId_, 33 | minRequiredBalance_, 34 | delegatedAccessRegistryAddress, 35 | stakingEnabled_ 36 | ); 37 | address deployedPolicy = address(instance); 38 | registry.register( 39 | tokenAddress, 40 | streamId_, 41 | deployedPolicy, 42 | tokenId_, 43 | stakingEnabled_ 44 | ); 45 | } 46 | } -------------------------------------------------------------------------------- /packages/chat-contracts/contracts/Factories/ERC20PolicyFactory.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Unlicense 2 | pragma solidity ^0.8.9; 3 | 4 | import "./TokenGateFactory.sol"; 5 | import "../JoinPolicies/ERC20JoinPolicy.sol"; 6 | 7 | contract ERC20PolicyFactory is TokenGateFactory{ 8 | 9 | constructor( 10 | address joinPolicyRegistryAddress_, 11 | address streamRegistryAddress_, 12 | address delegatedAccessRegistryAddress_ 13 | ) TokenGateFactory( 14 | joinPolicyRegistryAddress_, 15 | streamRegistryAddress_, 16 | delegatedAccessRegistryAddress_ 17 | ){} 18 | 19 | function create( 20 | address tokenAddress, 21 | string memory streamId_, 22 | uint256 minRequiredBalance_, 23 | uint256 /*tokenId_*/, 24 | bool stakingEnabled_, 25 | StreamRegistryV3.PermissionType[] memory defaultPermissions_ 26 | ) public override { 27 | ERC20JoinPolicy instance = new ERC20JoinPolicy( 28 | tokenAddress, 29 | streamRegistryAddress, 30 | streamId_, 31 | defaultPermissions_, 32 | minRequiredBalance_, 33 | delegatedAccessRegistryAddress, 34 | stakingEnabled_ 35 | ); 36 | address deployedPolicy = address(instance); 37 | registry.register( 38 | tokenAddress, 39 | streamId_, 40 | deployedPolicy, 41 | 0, // tokenId = 0 for ERC20 42 | stakingEnabled_ 43 | ); 44 | } 45 | } -------------------------------------------------------------------------------- /packages/chat-contracts/contracts/Factories/ERC721PolicyFactory.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Unlicense 2 | pragma solidity ^0.8.9; 3 | 4 | import "./TokenGateFactory.sol"; 5 | import "../JoinPolicies/ERC721JoinPolicy.sol"; 6 | 7 | contract ERC721PolicyFactory is TokenGateFactory{ 8 | 9 | constructor( 10 | address joinPolicyRegistryAddress_, 11 | address streamRegistryAddress_, 12 | address delegatedAccessRegistryAddress_ 13 | ) TokenGateFactory( 14 | joinPolicyRegistryAddress_, 15 | streamRegistryAddress_, 16 | delegatedAccessRegistryAddress_ 17 | ){} 18 | 19 | function create( 20 | address tokenAddress, 21 | string memory streamId_, 22 | uint256 /*minRequiredBalance_*/, 23 | uint256 tokenId_, 24 | bool stakingEnabled_, 25 | StreamRegistryV3.PermissionType[] memory defaultPermissions_ 26 | ) public override { 27 | ERC721JoinPolicy instance = new ERC721JoinPolicy( 28 | tokenAddress, 29 | streamRegistryAddress, 30 | streamId_, 31 | defaultPermissions_, 32 | tokenId_, 33 | delegatedAccessRegistryAddress, 34 | stakingEnabled_ 35 | ); 36 | address deployedPolicy = address(instance); 37 | registry.register( 38 | tokenAddress, 39 | streamId_, 40 | deployedPolicy, 41 | tokenId_, 42 | stakingEnabled_ 43 | ); 44 | } 45 | } -------------------------------------------------------------------------------- /packages/chat-contracts/contracts/Factories/ERC777PolicyFactory.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Unlicense 2 | pragma solidity ^0.8.9; 3 | 4 | import "./TokenGateFactory.sol"; 5 | import "../JoinPolicies/ERC777JoinPolicy.sol"; 6 | 7 | contract ERC777PolicyFactory is TokenGateFactory{ 8 | 9 | constructor( 10 | address joinPolicyRegistryAddress_, 11 | address streamRegistryAddress_, 12 | address delegatedAccessRegistryAddress_ 13 | ) TokenGateFactory( 14 | joinPolicyRegistryAddress_, 15 | streamRegistryAddress_, 16 | delegatedAccessRegistryAddress_ 17 | ){} 18 | 19 | function create( 20 | address tokenAddress, 21 | string memory streamId_, 22 | uint256 minRequiredBalance_, 23 | uint256 /*tokenId_*/, 24 | bool stakingEnabled_, 25 | StreamRegistryV3.PermissionType[] memory defaultPermissions_ 26 | ) public override { 27 | ERC777JoinPolicy instance = new ERC777JoinPolicy( 28 | tokenAddress, 29 | streamRegistryAddress, 30 | streamId_, 31 | defaultPermissions_, 32 | minRequiredBalance_, 33 | delegatedAccessRegistryAddress, 34 | stakingEnabled_ 35 | ); 36 | address deployedPolicy = address(instance); 37 | registry.register( 38 | tokenAddress, 39 | streamId_, 40 | deployedPolicy, 41 | 0, // tokenId = 0 for ERC777 42 | stakingEnabled_ 43 | ); 44 | } 45 | } -------------------------------------------------------------------------------- /packages/chat-contracts/contracts/Factories/TokenGateFactory.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Unlicense 2 | pragma solidity ^0.8.9; 3 | 4 | import "../JoinPolicyRegistry.sol"; 5 | 6 | abstract contract TokenGateFactory { 7 | JoinPolicyRegistry public registry; 8 | 9 | address public streamRegistryAddress; 10 | StreamRegistryV3 public streamRegistry; 11 | 12 | address public delegatedAccessRegistryAddress; 13 | 14 | constructor( 15 | address joinPolicyRegistryAddress_, 16 | address streamRegistryAddress_, 17 | address delegatedAccessRegistryAddress_ 18 | ) { 19 | registry = JoinPolicyRegistry(joinPolicyRegistryAddress_); 20 | streamRegistryAddress = streamRegistryAddress_; 21 | delegatedAccessRegistryAddress = delegatedAccessRegistryAddress_; 22 | } 23 | 24 | function create( 25 | address tokenAddress, 26 | string memory streamId_, 27 | uint256 minRequiredBalance_, 28 | uint256 tokenId_, 29 | bool stakingEnabled_, 30 | StreamRegistryV3.PermissionType[] memory defaultPermissions_ 31 | ) public virtual; 32 | } -------------------------------------------------------------------------------- /packages/chat-contracts/contracts/JoinPolicies/ERC20JoinPolicy.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: Unlicense 2 | pragma solidity ^0.8.9; 3 | 4 | import "@openzeppelin/contracts/utils/math/SafeMath.sol"; 5 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 6 | import "@streamr/network-contracts/contracts/StreamRegistry/StreamRegistryV3.sol"; 7 | import "./JoinPolicy.sol"; 8 | import "../DelegatedAccessRegistry.sol"; 9 | 10 | contract ERC20JoinPolicy is JoinPolicy{ 11 | IERC20 public token; 12 | 13 | constructor( 14 | address tokenAddress, 15 | address streamRegistryAddress, 16 | string memory streamId_, 17 | StreamRegistryV3.PermissionType[] memory permissions_, 18 | uint256 minRequiredBalance_, 19 | address delegatedAccessRegistryAddress, 20 | bool stakingEnabled_ 21 | ) JoinPolicy ( 22 | streamRegistryAddress, 23 | delegatedAccessRegistryAddress, 24 | streamId_, 25 | permissions_, 26 | stakingEnabled_ 27 | ) { 28 | require(minRequiredBalance_ > 0, "error_minReqBalanceGt0"); 29 | token = IERC20(tokenAddress); 30 | minRequiredBalance = minRequiredBalance_; 31 | } 32 | 33 | modifier canJoin() override { 34 | require(token.balanceOf(msg.sender) >= minRequiredBalance, "error_notEnoughTokens"); 35 | _; 36 | } 37 | 38 | function depositStake( 39 | uint256 amount 40 | ) 41 | override 42 | public 43 | isStakingEnabled() 44 | isUserAuthorized() 45 | canJoin() 46 | { 47 | token.transferFrom(msg.sender, address(this), amount); 48 | balances[msg.sender] = SafeMath.add(balances[msg.sender], amount); 49 | address delegatedWallet = delegatedAccessRegistry.getDelegatedWalletFor(msg.sender); 50 | accept(msg.sender, delegatedWallet); 51 | } 52 | 53 | function withdrawStake( 54 | uint256 amount 55 | ) 56 | override 57 | public 58 | isStakingEnabled() 59 | isUserAuthorized() 60 | { 61 | token.transfer(msg.sender, amount); 62 | balances[msg.sender] = SafeMath.sub(balances[msg.sender], amount); 63 | if (balances[msg.sender] < minRequiredBalance) { 64 | address delegatedWallet = delegatedAccessRegistry.getDelegatedWalletFor(msg.sender); 65 | revoke(msg.sender, delegatedWallet); 66 | } 67 | } 68 | 69 | } 70 | 71 | -------------------------------------------------------------------------------- /packages/chat-contracts/contracts/JoinPolicies/ERC721JoinPolicy.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: Unlicense 2 | pragma solidity ^0.8.9; 3 | 4 | import "@openzeppelin/contracts/token/ERC721/IERC721.sol"; 5 | import "@openzeppelin/contracts/token/ERC721/utils/ERC721Holder.sol"; 6 | import "@streamr/network-contracts/contracts/StreamRegistry/StreamRegistryV3.sol"; 7 | import "./JoinPolicy.sol"; 8 | import "../DelegatedAccessRegistry.sol"; 9 | 10 | contract ERC721JoinPolicy is JoinPolicy, ERC721Holder{ 11 | IERC721 public token; 12 | 13 | constructor( 14 | address tokenAddress, 15 | address streamRegistryAddress, 16 | string memory streamId_, 17 | StreamRegistryV3.PermissionType[] memory permissions_, 18 | uint256 tokenId_, 19 | address delegatedAccessRegistryAddress, 20 | bool stakingEnabled_ 21 | ) JoinPolicy( 22 | streamRegistryAddress, 23 | delegatedAccessRegistryAddress, 24 | streamId_, 25 | permissions_, 26 | stakingEnabled_ 27 | ) { 28 | token = IERC721(tokenAddress); 29 | tokenId = tokenId_; 30 | } 31 | 32 | modifier canJoin() override{ 33 | require(token.ownerOf(tokenId) == msg.sender, "error_notEnoughTokens"); 34 | _; 35 | } 36 | 37 | function depositStake( 38 | uint256 /*amount*/ 39 | ) 40 | override 41 | public 42 | isStakingEnabled() 43 | isUserAuthorized() 44 | canJoin() 45 | { 46 | token.safeTransferFrom(msg.sender, address(this), tokenId); 47 | balances[msg.sender] = 1; 48 | address delegatedWallet = delegatedAccessRegistry.getDelegatedWalletFor(msg.sender); 49 | accept(msg.sender, delegatedWallet); 50 | } 51 | 52 | function withdrawStake( 53 | uint256 /*amount*/ 54 | ) 55 | override 56 | public 57 | isStakingEnabled() 58 | isUserAuthorized() 59 | { 60 | token.safeTransferFrom(address(this), msg.sender, tokenId); 61 | balances[msg.sender] = 0; 62 | address delegatedWallet = delegatedAccessRegistry.getDelegatedWalletFor(msg.sender); 63 | revoke(msg.sender, delegatedWallet); 64 | } 65 | 66 | } -------------------------------------------------------------------------------- /packages/chat-contracts/contracts/JoinPolicyRegistry.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: Unlicense 2 | pragma solidity ^0.8.9; 3 | 4 | import "@streamr/network-contracts/contracts/StreamRegistry/StreamRegistryV3.sol"; 5 | import "./JoinPolicies/ERC20JoinPolicy.sol"; 6 | import "./JoinPolicies/ERC721JoinPolicy.sol"; 7 | import "./JoinPolicies/ERC1155JoinPolicy.sol"; 8 | 9 | contract JoinPolicyRegistry { 10 | // policyId => JoinPolicy 11 | mapping(bytes32 => address) public registeredPoliciesById; 12 | 13 | // tokenAddress => mapping(tokenId => mapping(streamId => mapping(isStakingEnabled => JoinPolicy))) 14 | mapping(address => mapping(uint256 => mapping(string => mapping(bool => address)))) public policies; 15 | 16 | event Registered( 17 | address indexed tokenAddress, 18 | string indexed streamId, 19 | bool indexed isStakingEnabled, 20 | address policyAddress, 21 | bytes32 policyId 22 | ); 23 | 24 | constructor(){} 25 | 26 | function canBeRegistered( 27 | address tokenAddress_, 28 | string memory streamId_, 29 | uint256 tokenId_, 30 | bool stakingEnabled_ 31 | ) public view returns (bytes32 policyId, bool) { 32 | policyId = keccak256(abi.encode(tokenAddress_, streamId_, tokenId_, stakingEnabled_)); 33 | return (policyId, registeredPoliciesById[policyId] == address(0x0)); 34 | } 35 | 36 | function register( 37 | address tokenAddress_, 38 | string memory streamId_, 39 | address deployedPolicy, 40 | uint256 tokenId_, 41 | bool stakingEnabled_ 42 | ) public { 43 | (bytes32 policyId, bool canBeRegistered_) = canBeRegistered(tokenAddress_, streamId_, tokenId_, stakingEnabled_); 44 | require(canBeRegistered_, "error_alreadyRegistered"); 45 | registeredPoliciesById[policyId] = deployedPolicy; 46 | 47 | policies[tokenAddress_][tokenId_][streamId_][stakingEnabled_] = deployedPolicy; 48 | emit Registered( 49 | tokenAddress_, 50 | streamId_, 51 | stakingEnabled_, 52 | deployedPolicy, 53 | policyId 54 | ); 55 | } 56 | 57 | // use tokenId_ = 0 for ERC20 and ERC777 58 | function getPolicy( 59 | address tokenAddress_, 60 | uint256 tokenId_, 61 | string memory streamId_, 62 | bool stakingEnabled_ 63 | ) public view returns (address) { 64 | return policies[tokenAddress_][tokenId_][streamId_][stakingEnabled_]; 65 | } 66 | } -------------------------------------------------------------------------------- /packages/chat-contracts/contracts/TestTokens/ERC1155TestToken.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Unlicense 2 | pragma solidity ^0.8.9; 3 | 4 | import "@openzeppelin/contracts/token/ERC1155/ERC1155.sol"; 5 | 6 | contract TestERC1155 is ERC1155 { 7 | constructor () ERC1155("TestToken") {} 8 | 9 | function mint(address account, uint256 id, uint256 amount) public { 10 | _mint(account, id, amount, "0x00"); 11 | } 12 | } -------------------------------------------------------------------------------- /packages/chat-contracts/contracts/TestTokens/ERC20TestToken.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Unlicense 2 | pragma solidity ^0.8.9; 3 | 4 | import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; 5 | 6 | contract TestERC20 is ERC20 { 7 | constructor () ERC20("TestToken", "TST") {} 8 | 9 | function mint(address account, uint256 amount) public { 10 | _mint(account, amount); 11 | } 12 | } -------------------------------------------------------------------------------- /packages/chat-contracts/contracts/TestTokens/ERC721TestToken.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Unlicense 2 | pragma solidity ^0.8.9; 3 | 4 | import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; 5 | 6 | contract TestERC721 is ERC721 { 7 | constructor () ERC721("TestToken", "TST") {} 8 | 9 | function mint(address account, uint256 tokenId) public { 10 | _mint(account, tokenId); 11 | } 12 | } -------------------------------------------------------------------------------- /packages/chat-contracts/contracts/TestTokens/ERC777TestToken.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Unlicense 2 | pragma solidity ^0.8.9; 3 | 4 | import "@openzeppelin/contracts/token/ERC777/ERC777.sol"; 5 | 6 | contract TestERC777 is ERC777 { 7 | address[] public _defaultOperators = [address(this)]; 8 | constructor () ERC777("TestToken", "TST", _defaultOperators) {} 9 | 10 | function mint(address account, uint256 amount) public { 11 | _mint(account, amount, "", ""); 12 | } 13 | } -------------------------------------------------------------------------------- /packages/chat-contracts/deployedAddresses.json: -------------------------------------------------------------------------------- 1 | { 2 | "polygon": { 3 | "chainId": 137, 4 | "url": "https://polygon-rpc.com", 5 | "environment": "production", 6 | "contracts": { 7 | "storageNodeRegistry": "0x080F34fec2bc33928999Ea9e39ADc798bEF3E0d6", 8 | "chainlinkOracle": "0x36BF71D0ba2e449fc14f9C4cF51468948E4ED27D", 9 | "enscache": "0x870528c1aDe8f5eB4676AA2d15FC0B034E276A1A", 10 | "streamRegistry": "0x0D483E10612F327FC11965Fc82E90dC19b141641", 11 | "streamStorageRegistry": "0xe8e2660CeDf2a59C917a5ED05B72df4146b58399" 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /packages/chat-contracts/scripts/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "no-console": "off", 4 | "object-curly-newline": "off", 5 | "max-len": ["warn", { 6 | "code": 150 7 | }] 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /packages/chat-contracts/scripts/1_deployDelegatedAccessRegistry.ts: -------------------------------------------------------------------------------- 1 | import { JsonRpcProvider } from '@ethersproject/providers' 2 | import { Wallet } from 'ethers' 3 | import hhat from 'hardhat' 4 | const { ethers } = hhat 5 | 6 | // localsidechain 7 | const chainURL = 'http://10.200.10.1:8546' 8 | const privKeyStreamRegistry = '' 9 | 10 | // polygon 11 | /* 12 | const chainURL = 'https://polygon-rpc.com' 13 | const privKeyStreamRegistry = process.env.PRIV_KEY || '' 14 | */ 15 | 16 | async function main() { 17 | const wallet = new Wallet(privKeyStreamRegistry, new JsonRpcProvider(chainURL)) 18 | 19 | const DelegatedAccessRegistry = await ethers.getContractFactory('DelegatedAccessRegistry', wallet) 20 | 21 | const tx = await DelegatedAccessRegistry.deploy() 22 | 23 | const instance = await tx.deployed() 24 | 25 | console.log(`DelegatedAccessRegistry deployed at ${instance.address}`) 26 | 27 | } 28 | 29 | main() -------------------------------------------------------------------------------- /packages/chat-contracts/scripts/2_deployJoinPolicyRegistry.ts: -------------------------------------------------------------------------------- 1 | import { JsonRpcProvider } from '@ethersproject/providers' 2 | import { BigNumber, Wallet } from 'ethers' 3 | import hhat from 'hardhat' 4 | const { ethers } = hhat 5 | import axios from 'axios' 6 | 7 | // localsidechain 8 | const chainURL = 'http://10.200.10.1:8546' 9 | const privKeyStreamRegistry = '' 10 | 11 | // polygon 12 | /* 13 | const chainURL = 'https://polygon-rpc.com' 14 | const privKeyStreamRegistry = process.env.KEY || '' 15 | */ 16 | 17 | let wallet: Wallet 18 | 19 | async function getGasStationPrices(): Promise<{maxFeePerGas: BigNumber, maxPriorityFeePerGas: BigNumber}> { 20 | const { data } = await axios({ 21 | method: 'get', 22 | url: 'https://gasstation-mainnet.matic.network/v2' 23 | }) 24 | const maxFeePerGas = ethers.utils.parseUnits( 25 | Math.ceil(data.fast.maxFee) + '', 26 | 'gwei' 27 | ) 28 | const maxPriorityFeePerGas = ethers.utils.parseUnits( 29 | Math.ceil(data.fast.maxPriorityFee) + '', 30 | 'gwei' 31 | ) 32 | 33 | return { maxFeePerGas, maxPriorityFeePerGas } 34 | } 35 | 36 | async function deployJoinPolicyRegistry(){ 37 | const JoinPolicyRegistry = await ethers.getContractFactory('JoinPolicyRegistry', wallet) 38 | const { maxFeePerGas, maxPriorityFeePerGas } = await getGasStationPrices() 39 | 40 | const tx = await JoinPolicyRegistry.deploy({ 41 | maxFeePerGas, maxPriorityFeePerGas 42 | 43 | }) 44 | 45 | const instance = await tx.deployed() 46 | 47 | console.log(`JoinPolicyRegistry deployed at ${instance.address}`) 48 | } 49 | 50 | async function main() { 51 | wallet = new Wallet(privKeyStreamRegistry, new JsonRpcProvider(chainURL)) 52 | console.log(`wallet address ${wallet.address}`) 53 | 54 | await deployJoinPolicyRegistry() 55 | } 56 | 57 | main() 58 | -------------------------------------------------------------------------------- /packages/chat-contracts/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "display": "Node 12", 4 | "compilerOptions": { 5 | "lib": ["es2019", "es2020.promise", "es2020.bigint", "es2020.string"], 6 | "module": "CommonJS", 7 | "target": "es2019", 8 | "preserveSymlinks": true, 9 | "strict": true, 10 | "esModuleInterop": true, 11 | "skipLibCheck": true, 12 | "forceConsistentCasingInFileNames": true, 13 | "resolveJsonModule": true, 14 | "outDir": "dist" 15 | }, 16 | "include": ["./scripts", "./test", "./typechain", "test-contracts"], 17 | "files": [ 18 | "./hardhat.config.ts" 19 | ] 20 | } -------------------------------------------------------------------------------- /packages/config/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | /dist 3 | /*.tgz 4 | src/generated 5 | -------------------------------------------------------------------------------- /packages/config/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2022 Kare Nuorteva, Streamr Network 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 | -------------------------------------------------------------------------------- /packages/config/Makefile: -------------------------------------------------------------------------------- 1 | LANG := en_US.UTF-8 2 | SHELL := /bin/bash 3 | .SHELLFLAGS := --norc --noprofile -e -u -o pipefail -c 4 | .DEFAULT_GOAL := test 5 | 6 | nvm_brew = /usr/local/opt/nvm/nvm.sh 7 | ifneq ("$(wildcard $(nvm_brew))", "") 8 | nvm_sh = $(nvm_brew) 9 | endif 10 | nvm_default = $(HOME)/.nvm/nvm.sh 11 | ifneq ("$(wildcard $(nvm_default))", "") 12 | nvm_sh = $(nvm_default) 13 | endif 14 | export NODE_VERSION = $(shell cat ../../.nvmrc) 15 | define npm 16 | @$(eval npm_args=$(1)) 17 | bash --norc --noprofile -e -o pipefail -l -c "source $(nvm_sh) && nvm exec npm $(npm_args)" 18 | endef 19 | export NODE_ENV := "development" 20 | 21 | .PHONY: test 22 | test: ## Run tests 23 | $(call npm, test) 24 | 25 | .PHONY: lint 26 | lint: ## Run lint 27 | $(call npm, run lint) 28 | 29 | .PHONY: build 30 | build: NODE_ENV = "production" 31 | build: ## Run build 32 | $(call npm, run build) 33 | 34 | .PHONY: npm-install 35 | npm-install: 36 | $(call npm, install) 37 | 38 | .PHONY: npm-publish 39 | npm-publish: 40 | $(call npm, publish . --access public) 41 | 42 | .PHONY: npm-pack 43 | npm-pack: 44 | $(call npm, pack) 45 | 46 | .PHONY: clean 47 | clean: ## Remove generated files 48 | $(RM) -r \ 49 | build \ 50 | dist \ 51 | node_modules 52 | 53 | .PHONY: help 54 | help: ## Show Help 55 | @grep -E '^[a-zA-Z0-9_\-\/]+%?:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "%-20s %s\n", $$1, $$2}' | sort 56 | -------------------------------------------------------------------------------- /packages/config/docs/draft-release-github.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/streamr-dev/network-contracts/62517cd6d0653049ac1f25ccb521e75a66c98b62/packages/config/docs/draft-release-github.png -------------------------------------------------------------------------------- /packages/config/mocharc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extension": ["ts"], 3 | "spec": "test/**/*.ts", 4 | "require": "ts-node/register" 5 | } 6 | -------------------------------------------------------------------------------- /packages/config/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "5.7.1", 3 | "name": "@streamr/config", 4 | "description": "Zero dependency package that contains Streamr Network smart contract addresses", 5 | "author": "Streamr Network AG ", 6 | "license": "STREAMR NETWORK OPEN SOURCE LICENSE", 7 | "private": false, 8 | "publishConfig": { 9 | "access": "public" 10 | }, 11 | "main": "dist/src/index.js", 12 | "types": "dist/src/index.d.ts", 13 | "directories": { 14 | "lib": "./dist", 15 | "src": "./src", 16 | "test": "./test" 17 | }, 18 | "files": [ 19 | "dist", 20 | "config.json" 21 | ], 22 | "scripts": { 23 | "clean": "rm -rf dist src/generated", 24 | "generate-types": "node ./scripts/generate-types.mjs", 25 | "build": "npm run generate-types && tsc --project tsconfig.json", 26 | "pretest": "npm run generate-types && tsc --project tsconfig-test.json", 27 | "test": "mocha --config mocharc.json" 28 | }, 29 | "repository": { 30 | "type": "git", 31 | "url": "git+https://github.com/streamr-dev/network-contracts.git" 32 | }, 33 | "keywords": [ 34 | "streamr network", 35 | "ethereum", 36 | "ethereum address", 37 | "smart contract", 38 | "smart contract address" 39 | ], 40 | "bugs": { 41 | "url": "https://github.com/streamr-dev/network-contracts/issues" 42 | }, 43 | "homepage": "https://github.com/streamr-dev/network-contracts/tree/master/packages/config#readme" 44 | } 45 | -------------------------------------------------------------------------------- /packages/config/release-git-tag.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e -u -o pipefail 4 | 5 | function usage() { 6 | local name 7 | name=$(basename "$0") 8 | echo "$name: Tag new version in Git" 1>&2 9 | echo "Usage: $name [-h] semver" 1>&2 10 | echo " -h Show help" 1>&2 11 | echo "Example: $name 0.2.7" 1>&2 12 | } 13 | 14 | while getopts "h" arg; do 15 | case $arg in 16 | h|*) # Show help 17 | usage 18 | exit 1 19 | ;; 20 | esac 21 | done 22 | 23 | version="${1-}" 24 | if test -z "$version"; then 25 | usage 26 | exit 1 27 | fi 28 | 29 | ./release-validate-semver.bash "$version" 30 | 31 | ./release-npm-update-version-package-json.bash "$version" 32 | 33 | # Create release commit 34 | git commit --message="release(config): @streamr/config ${version}" 35 | git push 36 | # Create tag 37 | git tag --message="Release config/${version}" --annotate "config/v${version}" 38 | git push origin "config/v${version}" 39 | -------------------------------------------------------------------------------- /packages/config/release-git-validate.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e -u -o pipefail 4 | 5 | function usage() { 6 | local name 7 | name=$(basename "$0") 8 | echo "$name: Validate Git repository is clean" 1>&2 9 | echo "Usage: $name [-h]" 1>&2 10 | echo " -h Show help" 1>&2 11 | echo "Example: $name" 1>&2 12 | } 13 | 14 | while getopts "h" arg; do 15 | case $arg in 16 | h|*) # Show help 17 | usage 18 | exit 1 19 | ;; 20 | esac 21 | done 22 | 23 | # check for unstaged changes 24 | if ! git diff --exit-code --quiet; then 25 | echo "$(basename "$0"): error: git workspace is dirty" 1>&2 26 | exit 1 27 | fi 28 | # check for staged, but not committed changes 29 | if ! git diff --cached --exit-code --quiet; then 30 | echo "$(basename "$0"): error: git workspace is dirty" 1>&2 31 | exit 1 32 | fi 33 | # check for no changes to be committed 34 | if output=$(git status --porcelain=v1) && test -n "$output"; then 35 | echo "$(basename "$0"): error: git workspace is dirty" 1>&2 36 | exit 1 37 | fi 38 | -------------------------------------------------------------------------------- /packages/config/release-npm-update-version-package-json.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e -u -o pipefail 4 | 5 | function usage() { 6 | local name 7 | name=$(basename "$0") 8 | echo "$name: Update version in package.json" 1>&2 9 | echo "Usage: $name [-h] semver" 1>&2 10 | echo " -h Show help" 1>&2 11 | echo "Example: $name 0.2.7" 1>&2 12 | } 13 | 14 | while getopts "h" arg; do 15 | case $arg in 16 | h|*) # Show help 17 | usage 18 | exit 1 19 | ;; 20 | esac 21 | done 22 | 23 | version="${1-}" 24 | if test -z "$version"; then 25 | usage 26 | exit 1 27 | fi 28 | 29 | ./release-validate-semver.bash "$version" 30 | 31 | sed -i '' -e 's/"version": ".*",$/"version": "'"$version"'",/g' package.json 32 | git add package.json 33 | make npm-install -C ../.. 34 | git add ../../package-lock.json 35 | -------------------------------------------------------------------------------- /packages/config/release-validate-semver.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e -u -o pipefail 4 | 5 | function usage() { 6 | local name 7 | name=$(basename "$0") 8 | echo "$name: Validate semantic version. Exists with error code 1 on validation error." 1>&2 9 | echo "Usage: $name [-h] semver" 1>&2 10 | echo " -h Show help" 1>&2 11 | echo "Example: $name 0.2.7" 1>&2 12 | } 13 | 14 | SEMVER_REGEX="^(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)(\\-[0-9A-Za-z-]+(\\.[0-9A-Za-z-]+)*)?(\\+[0-9A-Za-z-]+(\\.[0-9A-Za-z-]+)*)?$" 15 | function validate_version { 16 | local version=$1 17 | if ! [[ "$version" =~ $SEMVER_REGEX ]]; then 18 | echo "$(basename "$0"): error: version $version does not match the semver scheme 'X.Y.Z(-PRERELEASE)(+BUILD)'." 1>&2 19 | exit 1 20 | fi 21 | } 22 | 23 | while getopts "h" arg; do 24 | case $arg in 25 | h|*) # Show help 26 | usage 27 | exit 1 28 | ;; 29 | esac 30 | done 31 | 32 | version="${1-}" 33 | if test -z "$version"; then 34 | usage 35 | exit 1 36 | fi 37 | 38 | validate_version "$version" -------------------------------------------------------------------------------- /packages/config/release.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e -u -o pipefail 4 | 5 | function usage() { 6 | local name 7 | name=$(basename "$0") 8 | echo "$name: Publish new version" 1>&2 9 | echo "Usage: $name [-h] semver" 1>&2 10 | echo " -h Show help" 1>&2 11 | echo "Example: $name 0.2.7" 1>&2 12 | } 13 | 14 | while getopts "h" arg; do 15 | case $arg in 16 | h|*) # Show help 17 | usage 18 | exit 1 19 | ;; 20 | esac 21 | done 22 | 23 | make clean 24 | ./release-git-validate.bash 25 | 26 | version="${1-}" 27 | if test -z "$version"; then 28 | usage 29 | exit 1 30 | fi 31 | 32 | ./release-validate-semver.bash "$version" 33 | ./release-git-tag.bash "$version" 34 | make build 35 | make npm-publish 36 | -------------------------------------------------------------------------------- /packages/config/scripts/generate-types.mjs: -------------------------------------------------------------------------------- 1 | import prettier from 'prettier' 2 | import fs from 'fs' 3 | 4 | const json = JSON.parse(fs.readFileSync('config.json', 'utf8')) 5 | 6 | const items = [ 7 | '/* eslint-disable */', 8 | '/* This file has been generated with `npm run generate-types` */', 9 | '', 10 | `export const config = ${JSON.stringify(json)} as const`, 11 | ] 12 | 13 | const code = items.join('\n') 14 | 15 | const formattedCode = await prettier.format(code, { 16 | parser: 'typescript', 17 | singleQuote: true, 18 | semi: false, 19 | tabWidth: 4, 20 | }) 21 | 22 | if (!fs.existsSync('./src/generated')) { 23 | fs.mkdirSync('./src/generated') 24 | } 25 | 26 | fs.writeFileSync('./src/generated/config.ts', formattedCode) 27 | -------------------------------------------------------------------------------- /packages/config/src/index.ts: -------------------------------------------------------------------------------- 1 | import { config } from './generated/config' 2 | 3 | type ContractAddressKey = typeof config extends Record< 4 | any, 5 | Record<'contracts', Partial>> 6 | > 7 | ? K 8 | : never 9 | 10 | export type Chain = Readonly<{ 11 | adminPrivateKey?: string 12 | blockExplorerUrl?: string 13 | contracts: Partial> 14 | entryPoints?: Readonly< 15 | { 16 | nodeId: string 17 | websocket: Readonly<{ 18 | host: string 19 | port: number 20 | tls: boolean 21 | }> 22 | }[] 23 | > 24 | id: number 25 | name: string 26 | nativeCurrency: Readonly<{ 27 | decimals: number 28 | name: string 29 | symbol: string 30 | }> 31 | rpcEndpoints: Readonly< 32 | { 33 | url: string 34 | }[] 35 | > 36 | theGraphUrl?: string 37 | }> 38 | 39 | export type ChainKey = typeof config extends Record 40 | ? Key 41 | : never 42 | 43 | export type Config = Record 44 | 45 | export { config } 46 | 47 | /** 48 | * The following line makes sure the format of the source json file matches 49 | * the types. It's a validation step. Keep it. 50 | */ 51 | config as Config 52 | -------------------------------------------------------------------------------- /packages/config/test/index.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, it } from "mocha" 2 | import { assert } from "chai" 3 | import { config } from "../src" 4 | 5 | describe("Load configuration from JSON file", () => { 6 | it("ethereum chain id is 1", () => { 7 | const chainId: number = config.ethereum.id 8 | const expected = 1 9 | assert.equal(chainId, expected, `Expecting ethereum prod chain id to equal ${expected}, got '${chainId}'`) 10 | }) 11 | it("development chain id is 8995", () => { 12 | const chainId: number = config.dev0.id 13 | const expected = 8995 14 | assert.equal(chainId, expected, `Expecting ethereum dev chain id to equal ${expected}, got '${chainId}'`) 15 | }) 16 | it("reads DATA token dev address from JSON", () => { 17 | const address = config.dev0.contracts["DATA"] 18 | const expected = "0xbAA81A0179015bE47Ad439566374F2Bae098686F" 19 | assert.equal(address, expected, `Expecting ethereum DATA token to equal ${expected}, got '${address}'`) 20 | }) 21 | it("reads prod Polygon RPC URL", () => { 22 | const rpcHttpUrl = config.polygon.rpcEndpoints[0].url 23 | const expected = "https://polygon-rpc.com" 24 | assert.equal(rpcHttpUrl, expected, `Expecting prod polygon RPC URL to equal ${expected}, got '${rpcHttpUrl}'`) 25 | }) 26 | }) 27 | -------------------------------------------------------------------------------- /packages/dev-chain-fast/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:18-slim 2 | RUN apt-get update && apt-get install curl -y 3 | WORKDIR /usr/src/hardhat 4 | COPY ./dist ./package.json ./*.tgz ./hardhat.config.js ./start.sh ./ 5 | RUN npm i --omit=dev ./streamr-network-contracts-*.tgz ./streamr-hub-contracts-*.tgz 6 | 7 | CMD ["/bin/bash", "/usr/src/hardhat/start.sh"] 8 | # ENTRYPOINT [ "/bin/bash" ] 9 | 10 | 11 | -------------------------------------------------------------------------------- /packages/dev-chain-fast/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@streamr/dev-chain-fast", 3 | "version": "0.1.0", 4 | "description": "Locally run Ethereum client for developing the Streamr services", 5 | "author": "Streamr Network AG ", 6 | "license": "STREAMR NETWORK OPEN SOURCE LICENSE", 7 | "private": true, 8 | "files": [ 9 | "dist", 10 | "Dockerfile" 11 | ], 12 | "scripts": { 13 | "docker:buildLocalArch": "./prepare.sh && docker build -t streamr/dev-chain-fast:dev . && rm *.tgz", 14 | "docker:buildMultiArchAndPush": "./prepare.sh && docker buildx build --platform linux/amd64,linux/arm64 -t streamr/dev-chain-fast:dev --push . && rm *.tgz", 15 | "docker:buildStepOnly": "docker build -t streamr/dev-chain-fast:dev .", 16 | "preparebuild": "./prepare.sh", 17 | "deploy": "tsx ./src/deploy.ts", 18 | "clean": "rm -fr node_modules dist", 19 | "build": "tsc -p tsconfig.build.json" 20 | }, 21 | "dependencies": { 22 | "@streamr/hub-contracts": "file:../hub-contracts", 23 | "@streamr/network-contracts": "file:../network-contracts", 24 | "@dataunions/contracts": "^3.0.8", 25 | "hardhat": "2.22.9" 26 | }, 27 | "devDependencies": { 28 | "tsx": "^3.12.7", 29 | "ts-node": "^10.9.1", 30 | "typescript": "^5.1.3" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /packages/dev-chain-fast/prepare.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -ex 3 | 4 | # start in this script's directory 5 | cd "$(dirname "$0")" 6 | 7 | npm run clean 8 | rm -f streamr-network-contracts-*.tgz 9 | rm -f streamr-hub-contracts-*.tgz 10 | cd ../.. 11 | npm run build -w @streamr/network-contracts 12 | npm run build -w @streamr/hub-contracts 13 | cd packages/network-contracts 14 | npm pack 15 | mv streamr-network-contracts-*.tgz ../dev-chain-fast 16 | cd ../hub-contracts 17 | npm pack 18 | mv streamr-hub-contracts-*.tgz ../dev-chain-fast 19 | cd ../dev-chain-fast 20 | npm run build 21 | -------------------------------------------------------------------------------- /packages/dev-chain-fast/scripts/checkTimestamp.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env npx tsx 2 | 3 | import { providers } from "ethers" 4 | import { config } from "@streamr/config" 5 | 6 | const { 7 | CHAIN = "dev2", 8 | } = process.env 9 | 10 | const { 11 | rpcEndpoints: [{ url: rpcUrlFromConfig }], 12 | } = (config as any)[CHAIN] 13 | 14 | const { log } = console 15 | 16 | export async function logTimestamp(): Promise { 17 | const provider = new providers.JsonRpcProvider(rpcUrlFromConfig) 18 | log("Connected to %o", await provider.getNetwork()) 19 | const blockNumber = await provider.getBlockNumber() 20 | const block = await provider.getBlock(blockNumber) 21 | log("Timestamp from provider: %s (%s)", block.timestamp, new Date(block.timestamp * 1000).toISOString()) 22 | log("System time: %s (%s)", (Date.now() / 1000).toFixed(0), new Date().toISOString()) 23 | log("Difference: %s seconds", block.timestamp - (Date.now() / 1000)) 24 | } 25 | logTimestamp().catch(console.error) 26 | -------------------------------------------------------------------------------- /packages/dev-chain-fast/start.sh: -------------------------------------------------------------------------------- 1 | npx hardhat node & 2 | node deploy.js & 3 | # keep container running 4 | tail -f /dev/null 5 | -------------------------------------------------------------------------------- /packages/dev-chain-fast/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "display": "Node 12", 4 | "compilerOptions": { 5 | "lib": ["es2019", "es2020.promise", "es2020.bigint", "es2020.string"], 6 | "module": "CommonJS", 7 | "target": "es2019", 8 | "preserveSymlinks": true, 9 | "strict": true, 10 | "esModuleInterop": true, 11 | "skipLibCheck": true, 12 | "forceConsistentCasingInFileNames": true, 13 | "resolveJsonModule": true, 14 | "declaration": true, 15 | "outDir": "dist" 16 | }, 17 | "include": ["./src"] 18 | } 19 | -------------------------------------------------------------------------------- /packages/dev-chain-fast/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "display": "Node 12", 4 | "compilerOptions": { 5 | "lib": ["es2019", "es2020.promise", "es2020.bigint", "es2020.string"], 6 | "module": "CommonJS", 7 | "target": "es2019", 8 | "preserveSymlinks": true, 9 | "strict": true, 10 | "esModuleInterop": true, 11 | "skipLibCheck": true, 12 | "forceConsistentCasingInFileNames": true, 13 | "resolveJsonModule": true, 14 | "outDir": "dist" 15 | } 16 | } -------------------------------------------------------------------------------- /packages/ens-sync-script/.gitignore: -------------------------------------------------------------------------------- 1 | heartbeat* 2 | -------------------------------------------------------------------------------- /packages/ens-sync-script/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:18-slim 2 | 3 | # RUN apk add libsecret-dev 4 | # RUN apt update 5 | # RUN apt install -y libsecret-1-dev 6 | # RUN mkdir /firstrun && chown node:node /firstrun 7 | RUN mkdir -p /home/node/app/script && chown -R node:node /home/node/app 8 | WORKDIR /home/node/app/script 9 | 10 | USER node 11 | 12 | COPY --chown=node:node . . 13 | 14 | RUN mv network-contracts ../ 15 | 16 | RUN npm i 17 | ENV DEBUG=* 18 | CMD [ "npm", "run", "start" ] 19 | -------------------------------------------------------------------------------- /packages/ens-sync-script/README.md: -------------------------------------------------------------------------------- 1 | To test it first run 2 | 3 | ``` 4 | streamr-docker-dev start ens-sync-script 5 | ``` 6 | 7 | Then run this test in the network-contracts package: 8 | 9 | ``` 10 | npm run test:ens-e2e 11 | ``` 12 | -------------------------------------------------------------------------------- /packages/ens-sync-script/healthcheck.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # check if heartbeat file is newer than 5 minutes 4 | echo "healthcheck: checking heartbeat file" 5 | if [[ $(find heartbeat -mtime -0.0035) ]]; then 6 | exit 0 7 | fi 8 | exit 1 9 | -------------------------------------------------------------------------------- /packages/ens-sync-script/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ens-sync-script", 3 | "version": "1.0.0", 4 | "type": "module", 5 | "description": "looks up owner of ensname on stream creation and creates stream", 6 | "author": "Streamr Network AG ", 7 | "license": "STREAMR NETWORK OPEN SOURCE LICENSE", 8 | "main": "index.ts", 9 | "scripts": { 10 | "start": "tsx ./src/index.ts", 11 | "prepareArtifacts": "./prepare.sh", 12 | "buildLocalArch": "npm run prepareArtifacts && docker build -t streamr/ens-sync-script:dev-fastchain .", 13 | "buildMultiarchAndPush": "npm run prepareArtifacts && docker buildx build --platform linux/amd64,linux/arm64 -t streamr/ens-sync-script:dev-fastchain --push ." 14 | }, 15 | "dependencies": { 16 | "@ensdomains/ens-contracts": "^0.0.22", 17 | "@ethersproject/contracts": "^5.7.0", 18 | "@ethersproject/providers": "^5.7.2", 19 | "@ethersproject/units": "^5.7.0", 20 | "@ethersproject/wallet": "^5.7.0", 21 | "@streamr/config": "^5.3.7", 22 | "@streamr/network-contracts": "^7.0.8", 23 | "debug": "^4.3.4", 24 | "eth-ens-namehash": "^2.0.8", 25 | "node-fetch": "2.6.7", 26 | "tsx": "^4.7.0" 27 | }, 28 | "devDependencies": { 29 | "@typescript-eslint/eslint-plugin": "^5.57.1", 30 | "@typescript-eslint/parser": "^5.57.1", 31 | "eslint-config-streamr-nodejs": "^2.0.1", 32 | "typescript": "^5.0.3" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /packages/ens-sync-script/prepare.sh: -------------------------------------------------------------------------------- 1 | mkdir -p network-contracts/artifacts/contracts/StreamRegistry/StreamRegistryV5.sol 2 | mkdir -p network-contracts/artifacts/contracts/chainlinkClient/ENSCacheV2Streamr.sol 3 | cp ../network-contracts/artifacts/contracts/StreamRegistry/StreamRegistryV5.sol/StreamRegistryV5.json ./network-contracts/artifacts/contracts/StreamRegistry/StreamRegistryV5.sol 4 | cp -r ../network-contracts/artifacts/contracts/chainlinkClient/ENSCacheV2Streamr.sol/ENSCacheV2Streamr.json ./network-contracts/artifacts/contracts/chainlinkClient/ENSCacheV2Streamr.sol 5 | 6 | -------------------------------------------------------------------------------- /packages/ens-sync-script/src/eth-ens-namehash.d.ts: -------------------------------------------------------------------------------- 1 | declare module "eth-ens-namehash" { 2 | export function hash(inputName: string): string; 3 | export function normalize(name: string): string; 4 | } 5 | -------------------------------------------------------------------------------- /packages/ens-sync-script/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "display": "Node 20", 4 | "compilerOptions": { 5 | "lib": ["ESNext", "es2019", "es2020.promise", "es2020.bigint", "es2020.string"], 6 | "module": "ESNext", 7 | "moduleResolution": "Node", 8 | "target": "ES2022", 9 | "preserveSymlinks": true, 10 | "strict": true, 11 | "esModuleInterop": true, 12 | "noImplicitAny": true, 13 | "skipLibCheck": true, 14 | "forceConsistentCasingInFileNames": true, 15 | "resolveJsonModule": true, 16 | "outDir": "dist", 17 | "strictPropertyInitialization": false 18 | }, 19 | "include": [ 20 | "./src" 21 | ] 22 | } -------------------------------------------------------------------------------- /packages/hub-contracts/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .env 3 | coverage 4 | coverage.json 5 | typechain 6 | 7 | #Hardhat files 8 | cache 9 | artifacts 10 | -------------------------------------------------------------------------------- /packages/hub-contracts/.mocharc.json: -------------------------------------------------------------------------------- 1 | { 2 | "require": "ts-node/register/files", 3 | "timeout": 20000 4 | } -------------------------------------------------------------------------------- /packages/hub-contracts/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "overrides": [ 3 | { 4 | "files": "*.sol", 5 | "options": { 6 | "printWidth": 80, 7 | "tabWidth": 4, 8 | "useTabs": true, 9 | "singleQuote": false, 10 | "bracketSpacing": true, 11 | "explicitTypes": "always" 12 | } 13 | } 14 | ] 15 | } -------------------------------------------------------------------------------- /packages/hub-contracts/.solcover.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | skipFiles: [], 3 | configureYulOptimizer: true 4 | }; -------------------------------------------------------------------------------- /packages/hub-contracts/.solhint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "solhint:recommended", 3 | "rules": { 4 | "max-states-count": "off", 5 | "code-complexity": [ 6 | "error", 7 | 7 8 | ], 9 | "no-empty-blocks": "off", 10 | "no-unused-vars": "error", 11 | "payable-fallback": "error", 12 | "reason-string": [ 13 | "error", 14 | { 15 | "maxLength": 50 16 | } 17 | ], 18 | "no-global-import": "off", 19 | "constructor-syntax": "error", 20 | "const-name-snakecase": "error", 21 | "event-name-camelcase": "error", 22 | "func-name-mixedcase": "error", 23 | "func-param-name-mixedcase": "error", 24 | "modifier-name-mixedcase": "error", 25 | "use-forbidden-name": "error", 26 | "var-name-mixedcase": "error", 27 | "imports-on-top": "error", 28 | "visibility-modifier-order": "error", 29 | "avoid-call-value": "error", 30 | "avoid-low-level-calls": "error", 31 | "avoid-sha3": "error", 32 | "avoid-suicide": "error", 33 | "avoid-throw": "error", 34 | "avoid-tx-origin": "error", 35 | "check-send-result": "error", 36 | "multiple-sends": "error", 37 | "no-complex-fallback": "error", 38 | "no-inline-assembly": "error", 39 | "not-rely-on-block-hash": "error", 40 | "not-rely-on-time": "error", 41 | "reentrancy": "error", 42 | "state-visibility": "error", 43 | "func-visibility": [ 44 | "error", 45 | { 46 | "ignoreConstructors": true 47 | } 48 | ], 49 | "quotes": [ 50 | "error", 51 | "double" 52 | ], 53 | "max-line-length": [ 54 | "error", 55 | 200 56 | ], 57 | "compiler-version": [ 58 | "error", 59 | "^0.8.0" 60 | ] 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /packages/hub-contracts/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "node", 9 | "request": "launch", 10 | "name": "Hardhat test this file", 11 | "runtimeExecutable": "${workspaceFolder}/node_modules/.bin/hardhat", 12 | "env": { 13 | "DEBUG": "*,-mocha:*" 14 | }, 15 | "runtimeArgs": [ 16 | "test", 17 | "${file}" 18 | ], 19 | "internalConsoleOptions": "openOnSessionStart", 20 | "cwd": "${workspaceFolder}" 21 | } 22 | ] 23 | } -------------------------------------------------------------------------------- /packages/hub-contracts/contracts/Marketplace/IInterchainGasPaymaster.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.9; 4 | 5 | interface IInterchainGasPaymaster { 6 | function payForGas( 7 | bytes32 _messageId, 8 | uint32 _destinationDomain, 9 | uint256 _gasAmount, 10 | address _refundAddress 11 | ) external payable; 12 | 13 | function quoteGasPayment(uint32 _destinationDomain, uint256 _gasAmount) 14 | external 15 | view 16 | returns (uint256); 17 | } 18 | -------------------------------------------------------------------------------- /packages/hub-contracts/contracts/Marketplace/IInterchainQueryRouter.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.9; 4 | 5 | struct Call { 6 | address to; 7 | bytes data; 8 | } 9 | 10 | interface IInterchainQueryRouter { 11 | function query( 12 | uint32 _destinationDomain, 13 | Call calldata call, 14 | bytes calldata callback 15 | ) external returns (bytes32); 16 | } 17 | -------------------------------------------------------------------------------- /packages/hub-contracts/contracts/Marketplace/IMailbox.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.9; 4 | 5 | interface IMailbox { 6 | function dispatch( 7 | uint32 destinationDomainId, // the chain id where MarketplaceV4 is deployed and where messages are sent to 8 | bytes32 recipientAddress, // the address for the MarketplaceV4 contract. It must have the handle() function 9 | bytes calldata messageBody // encoded purchase info 10 | ) external returns (bytes32); 11 | } 12 | -------------------------------------------------------------------------------- /packages/hub-contracts/contracts/Marketplace/IMarketplaceV4.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.9; 4 | 5 | interface IMarketplaceV4 { 6 | // project events 7 | event ProjectPurchased(bytes32 projectId, address subscriber, uint256 subscriptionSeconds, uint256 price, uint256 fee); 8 | 9 | // txFee events 10 | event TxFeeChanged(uint256 indexed newTxFee); 11 | 12 | // admin functionality events 13 | event Halted(); 14 | event Resumed(); 15 | 16 | function buy(bytes32 projectId, uint subscriptionSeconds) external; 17 | function buyFor(bytes32 projectId, uint subscriptionSeconds, address recipient) external; 18 | function onTokenTransfer(address sender, uint256 amount, bytes calldata data) external; 19 | 20 | function addRemoteMarketplace(uint32 originDomainId, address remoteMarketplaceAddress) external; 21 | function getPurchaseInfo( 22 | bytes32 projectId, 23 | uint256 subscriptionSeconds, 24 | uint32 originDomainId, 25 | uint256 purchaseId 26 | ) external view returns(address, address, uint256, uint256, uint256, uint256); 27 | function getSubscriptionInfo( 28 | bytes32 projectId, 29 | address subscriber, 30 | uint256 purchaseId 31 | ) external view returns(bool, uint256, uint256); 32 | } 33 | -------------------------------------------------------------------------------- /packages/hub-contracts/contracts/Marketplace/IMessageRecipient.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.9; 4 | 5 | interface IMessageRecipient { 6 | function handle( 7 | uint32 _origin, // the chain id of the remote chain. Unique id assigned by Hyperlane (the same as the chainId in the EIP-155). 8 | bytes32 _sender, // the contract address on the remote chain (e.g. RemoteMarketplace). It must match or the message will revert 9 | bytes calldata _message // encoded purchase info 10 | ) external; 11 | } 12 | -------------------------------------------------------------------------------- /packages/hub-contracts/contracts/Marketplace/IPurchaseListener.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.9; 4 | 5 | interface IPurchaseListener { 6 | // TODO: find out about how to best detect who implements an interface 7 | // see at least https://github.com/ethereum/EIPs/blob/master/EIPS/eip-165.md 8 | // function isPurchaseListener() external returns (bool); 9 | 10 | /** 11 | * Similarly to ETH transfer, returning false will decline the transaction 12 | * (declining should probably cause revert, but that's up to the caller) 13 | * IMPORTANT: include onlyMarketplace modifier to your implementations! 14 | */ 15 | function onPurchase( 16 | bytes32 productId, 17 | address subscriber, 18 | uint256 endTimestamp, 19 | uint256 priceDatacoin, 20 | uint256 feeDatacoin 21 | ) external returns (bool accepted); 22 | } 23 | -------------------------------------------------------------------------------- /packages/hub-contracts/contracts/Marketplace/IRemoteMarketplaceV1.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.9; 4 | 5 | interface IRemoteMarketplaceV1 { 6 | struct ProjectPurchase { 7 | bytes32 projectId; 8 | address buyer; 9 | address subscriber; 10 | address beneficiary; 11 | address pricingTokenAddress; 12 | uint256 subscriptionSeconds; 13 | uint256 requestTimestamp; 14 | uint256 price; 15 | uint256 fee; 16 | } 17 | 18 | // purchase events 19 | event ProjectPurchasedFromRemote(bytes32 projectId, address subscriber, uint256 subscriptionSeconds, uint256 price, uint256 fee); 20 | 21 | // admin functions 22 | function addRecipient(uint32 _destinationDomainId, address _recipientContractAddress) external; 23 | 24 | // purchase functions 25 | function buy(bytes32 projectId, uint256 subscriptionSeconds) external; 26 | function buyFor(bytes32 projectId, uint256 subscriptionSeconds, address subscriber) external; 27 | 28 | // callback functions 29 | function handlePurchaseInfo( 30 | address beneficiary, 31 | address pricingTokenAddress, 32 | uint256 price, 33 | uint256 fee, 34 | uint256 purchaseId, 35 | uint256 streamsCount 36 | ) external; 37 | function handleSubscriptionState( 38 | bool isValid, 39 | uint256 subEndTimestamp, 40 | uint256 purchaseId 41 | ) external; 42 | 43 | receive() external payable; 44 | function withdraw(uint256 amount) external; 45 | } 46 | -------------------------------------------------------------------------------- /packages/hub-contracts/contracts/ProjectStaking/IProjectStakingV1.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.9; 4 | 5 | interface IProjectStakingV1 { 6 | // project staking events 7 | 8 | event Stake(bytes32 indexed projectId, address indexed user, uint256 amount); 9 | event Unstake(bytes32 indexed projectId, address indexed user, uint256 amount); 10 | 11 | // events after upgrade (0xfb5e20c0daf89b2fd026755d374d59b4e802ccdca6f6e3721691c0483ea9fdcd) 12 | event Stake(bytes32 indexed projectId, address indexed user, uint256 amount, uint256 projectStake); 13 | event Unstake(bytes32 indexed projectId, address indexed user, uint256 amount, uint256 projectStake); 14 | 15 | // project staking functions 16 | 17 | // view functions 18 | function getProjectStake(bytes32 projectId) external view returns (uint256 projectStake); 19 | function getUserStake(address userAddress) external view returns (uint256 userStake); 20 | function getTotalStake() external view returns (uint256 totalStake); 21 | 22 | // state changing functions 23 | function stake(bytes32 projectId, uint256 amount) external; 24 | function unstake(bytes32 projectId, uint256 amount) external; 25 | function onTokenTransfer(address sender, uint256 amount, bytes calldata data) external; 26 | } 27 | -------------------------------------------------------------------------------- /packages/hub-contracts/contracts/test-contracts/ERC20Mintable.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.9; 3 | import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; 4 | 5 | /** 6 | * ERC20Mintable is missing from open-zeppelin 7 | */ 8 | contract ERC20Mintable is ERC20 { 9 | address private creator; 10 | constructor() ERC20("Mintable Test Token", "TTT") { 11 | creator = msg.sender; 12 | } 13 | 14 | function mint(address receipient, uint amount) public { 15 | require(msg.sender == creator, "only_creator"); 16 | _mint(receipient, amount); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /packages/hub-contracts/contracts/test-contracts/MockMarketplaceBeneficiary.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.9; 4 | 5 | import "../Marketplace/IPurchaseListener.sol"; 6 | 7 | contract MockMarketplaceBeneficiary is IPurchaseListener { 8 | event OnTokenTransferCalled(address recipient, uint256 value, bytes data); 9 | event OnPurchaseCalled( 10 | bytes32 productId, 11 | address subscriber, 12 | uint256 endTimestamp, 13 | uint256 priceDatacoin, 14 | uint256 feeDatacoin 15 | ); 16 | 17 | function onTokenTransfer(address recipient, uint256 value, bytes calldata data) public { 18 | emit OnTokenTransferCalled(recipient, value, data); 19 | } 20 | 21 | function onPurchase( 22 | bytes32 productId, 23 | address subscriber, 24 | uint256 endTimestamp, 25 | uint256 priceDatacoin, 26 | uint256 feeDatacoin 27 | ) public returns (bool accepted) { 28 | emit OnPurchaseCalled(productId, subscriber, endTimestamp, priceDatacoin, feeDatacoin); 29 | accepted = (subscriber != address(0x1234567890123456789012345678901234567890)); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /packages/hub-contracts/contracts/token/IERC677.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 5 | 6 | // adapted from LINK token https://etherscan.io/address/0x514910771af9ca656af840dff83e8264ecf986ca#code 7 | // implements https://github.com/ethereum/EIPs/issues/677 8 | interface IERC677 is IERC20 { 9 | function transferAndCall( 10 | address to, 11 | uint value, 12 | bytes calldata data 13 | ) external returns (bool success); 14 | 15 | event Transfer( 16 | address indexed from, 17 | address indexed to, 18 | uint value, 19 | bytes data 20 | ); 21 | } 22 | -------------------------------------------------------------------------------- /packages/hub-contracts/contracts/token/IERC677Receiver.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | interface IERC677Receiver { 5 | function onTokenTransfer( 6 | address _sender, 7 | uint256 _value, 8 | bytes calldata _data 9 | ) external; 10 | } 11 | -------------------------------------------------------------------------------- /packages/hub-contracts/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@streamr/hub-contracts", 3 | "version": "1.1.2", 4 | "description": "Smart contracts for the Streamr multi-chain Hub", 5 | "author": "Streamr Network AG ", 6 | "license": "STREAMR NETWORK OPEN SOURCE LICENSE", 7 | "private": false, 8 | "publishConfig": { 9 | "access": "public" 10 | }, 11 | "files": [ 12 | "dist" 13 | ], 14 | "main": "./dist/src/exports.js", 15 | "scripts": { 16 | "build": "rm -rf artifacts && hardhat compile && rm -rf dist && tsc -p tsconfig.build.json", 17 | "clean": "rm -rf artifacts dist cache coverage typechain deployments .openzeppelin/unknown-31337.json", 18 | "eslint": "eslint {src,scripts,test}/**/*.ts", 19 | "lint": "solhint contracts/**/*.sol", 20 | "test": "hardhat test test/contracts/*", 21 | "deploy-registry": "hardhat run --network $CHAIN scripts/deployProjectRegistry.ts", 22 | "deploy-staking": "hardhat run --network $CHAIN scripts/deployProjectStakingV1.ts", 23 | "deploy-marketplace": "hardhat run --network $CHAIN scripts/deployMarketplaceV4.ts", 24 | "verify": "hardhat verify $ADDRESS --network $CHAIN --show-stack-traces", 25 | "interact": "hardhat run --network $CHAIN scripts/interactProjectRegistry.ts", 26 | "upgrade": "hardhat run --network $CHAIN scripts/upgradeMarketplaceV3.ts", 27 | "hubEnvDeployer": "npx tsx scripts/hubEnvDeployer.ts" 28 | }, 29 | "dependencies": { 30 | "@chainlink/contracts": "^0.4.2", 31 | "@jridgewell/trace-mapping": "0.3.9", 32 | "@openzeppelin/contracts": "~4.4.2", 33 | "@openzeppelin/contracts-upgradeable": "4.8.0", 34 | "@streamr/network-contracts": "7.1.1", 35 | "@streamr/config": "^5.0.0" 36 | }, 37 | "devDependencies": { 38 | "@uniswap/v2-core": "^1.0.1", 39 | "@uniswap/v2-periphery": "^1.1.0-beta.0", 40 | "node-fetch": "2.6.6", 41 | "streamr-client": "^6.0.9", 42 | "@hyperlane-xyz/core": "1.2.0", 43 | "@nomicfoundation/hardhat-chai-matchers": "^1.0.0", 44 | "@types/chai": "^4.3.0", 45 | "@types/debug": "^4.1.7", 46 | "@types/node": "^17.0.22", 47 | "hardhat": "^2.9.1", 48 | "hardhat-dependency-compiler": "^1.1.3", 49 | "ts-node": "^10.7.0", 50 | "typescript": "^4.6.2", 51 | "@nomicfoundation/hardhat-toolbox": "2.0.1", 52 | "@openzeppelin/hardhat-upgrades": "^1.18.3", 53 | "@types/jest": "^28.1.1", 54 | "@types/mocha": "^9.1.1", 55 | "mocha": "9.2.0", 56 | "solhint": "^3.3.7" 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /packages/hub-contracts/scripts/deployMarketplaceV3.ts: -------------------------------------------------------------------------------- 1 | import { ethers, upgrades } from "hardhat" 2 | 3 | const { log } = console 4 | 5 | /** 6 | * npx hardhat run --network dev1 scripts/deployMarketplaceV3.ts 7 | */ 8 | async function main() { 9 | const Marketplace = await ethers.getContractFactory("MarketplaceV3") 10 | const marketplace = await upgrades.deployProxy(Marketplace, [], { kind: 'uups' }) 11 | await marketplace.deployed() 12 | log(`MarketplaceV3 deployed at ${marketplace.address}`) 13 | } 14 | 15 | main().catch((error) => { 16 | console.error(error) 17 | process.exitCode = 1 18 | }) 19 | -------------------------------------------------------------------------------- /packages/hub-contracts/scripts/deployMarketplaceV4.ts: -------------------------------------------------------------------------------- 1 | import { ethers as hardhatEthers, upgrades } from "hardhat" 2 | import { config } from "@streamr/config" 3 | import { utils } from "ethers" 4 | import { chainToMailboxAddress } from "../utils" 5 | 6 | const { getContractFactory } = hardhatEthers 7 | const { id } = utils 8 | const { log } = console 9 | 10 | const { 11 | CHAIN = 'dev1', 12 | } = process.env 13 | 14 | const { 15 | id: CHAIN_ID, 16 | contracts: { 17 | ProjectRegistryV1: PROJECT_REGISTRY_ADDRESS, 18 | } 19 | } = (config as any)[CHAIN] 20 | if (!PROJECT_REGISTRY_ADDRESS) { throw new Error(`No ProjectRegistryV1 found in chain "${CHAIN}"`) } 21 | 22 | const interchainMailbox = chainToMailboxAddress(CHAIN) 23 | 24 | /** 25 | * npx hardhat run --network dev1 scripts/deployMarketplaceV4.ts 26 | * npx hardhat flatten contracts/MarketplaceV4.sol > mpv4.sol 27 | */ 28 | async function main() { 29 | log(`Deploying MarketplaceV4 to ${CHAIN}:`) 30 | log(` - project registry address: ${PROJECT_REGISTRY_ADDRESS}`) 31 | 32 | const projectRegistryFactory = await getContractFactory("ProjectRegistryV1") 33 | const projectRegistryFactoryTx = await projectRegistryFactory.attach(PROJECT_REGISTRY_ADDRESS) 34 | const projectRegistry = await projectRegistryFactoryTx.deployed() 35 | log("ProjectRegistryV1 attached at: ", projectRegistry.address) 36 | 37 | log(`Deploying MarketplaceV4 to ${CHAIN}:`) 38 | const Marketplace = await getContractFactory("MarketplaceV4") 39 | const marketplace = await upgrades.deployProxy(Marketplace, [projectRegistry.address, CHAIN_ID], { kind: 'uups' }) 40 | await marketplace.deployed() 41 | log(`MarketplaceV4 deployed on ${CHAIN} at: ${marketplace.address}`) 42 | 43 | await marketplace.addMailbox(interchainMailbox) 44 | log(`MarketplaceV4 added interchain mailbox: ${interchainMailbox}`) 45 | 46 | await projectRegistry.grantRole(id("TRUSTED_ROLE"), marketplace.address) 47 | log(`ProjectRegistry granted trusted role to MarketplaceV4.`) 48 | } 49 | 50 | main().catch((error) => { 51 | console.error(error) 52 | process.exitCode = 1 53 | }) 54 | -------------------------------------------------------------------------------- /packages/hub-contracts/scripts/deployProjectRegistry.ts: -------------------------------------------------------------------------------- 1 | import { ethers as hhEthers, upgrades } from "hardhat" 2 | import { config } from "@streamr/config" 3 | 4 | const { log } = console 5 | 6 | const { 7 | CHAIN = 'dev1', 8 | } = process.env 9 | 10 | const { 11 | contracts: { 12 | StreamRegistry: STREAM_REGISTRY_ADDRESS, // = 0x0000000000000000000000000000000000000000 13 | } 14 | } = (config as any)[CHAIN] 15 | 16 | if (!STREAM_REGISTRY_ADDRESS) { throw new Error(`No StreamRegistry found in chain "${CHAIN}"`) } 17 | // const STREAM_REGISTRY_ADDRESS = "0x0000000000000000000000000000000000000000" 18 | 19 | /** 20 | * npx hardhat run --network dev1 scripts/deployProjectRegistry.ts 21 | * npx hardhat flatten contracts/ProjectRegistry/ProjectRegistryV1.sol > pr.sol 22 | */ 23 | async function main() { 24 | log(`StreamRegistry address: ${STREAM_REGISTRY_ADDRESS}`) 25 | log(`Deploying ProjectRegistryV1 to "${CHAIN}" chain:`) 26 | const projectRegistryFactory = await hhEthers.getContractFactory("ProjectRegistryV1") 27 | const projectRegistryFactoryTx = await upgrades.deployProxy(projectRegistryFactory, [STREAM_REGISTRY_ADDRESS], { kind: 'uups' }) 28 | const projectRegistry = await projectRegistryFactoryTx.deployed() 29 | log(`ProjectRegistryV1 deployed at: ${projectRegistry.address}`) 30 | } 31 | 32 | main().catch((error) => { 33 | console.error(error) 34 | process.exitCode = 1 35 | }) 36 | -------------------------------------------------------------------------------- /packages/hub-contracts/scripts/deployProjectStakingV1.ts: -------------------------------------------------------------------------------- 1 | import { ethers as hhEthers, upgrades } from "hardhat" 2 | import { config } from "@streamr/config" 3 | 4 | const { log } = console 5 | 6 | const { 7 | CHAIN = 'dev1', 8 | } = process.env 9 | 10 | const { 11 | contracts: { 12 | DATA: STAKING_TOKEN_ADDRESS, // LINK dev1 - 0x3387F44140ea19100232873a5aAf9E46608c791E 13 | ProjectRegistryV1: PROJECT_REGISTRY_ADDRESS, 14 | } 15 | } = (config as any)[CHAIN] 16 | 17 | if (!PROJECT_REGISTRY_ADDRESS) { throw new Error(`No ProjectRegistryV1 found in chain "${CHAIN}"`) } 18 | 19 | /** 20 | * npx hardhat run --network dev1 scripts/deployProjectStakingV1.ts 21 | * npx hardhat flatten contracts/ProjectStaking/ProjectStakingV1.sol > ps.sol 22 | */ 23 | async function main() { 24 | log(`ProjectRegistryV1 address: ${PROJECT_REGISTRY_ADDRESS}`) 25 | log(`Staking token address: ${STAKING_TOKEN_ADDRESS}`) 26 | log(`Deploying ProjectStakingV1 to "${CHAIN}" chain:`) 27 | const projectStakingFactory = await hhEthers.getContractFactory("ProjectStakingV1") 28 | const projectStakingFactoryTx = await upgrades.deployProxy(projectStakingFactory, [ 29 | PROJECT_REGISTRY_ADDRESS, 30 | STAKING_TOKEN_ADDRESS 31 | ], { kind: 'uups' }) 32 | const projectStaking = await projectStakingFactoryTx.deployed() 33 | log(`ProjectStakingV1 deployed at: ${projectStaking.address}`) 34 | } 35 | 36 | main().catch((error) => { 37 | console.error(error) 38 | process.exitCode = 1 39 | }) 40 | -------------------------------------------------------------------------------- /packages/hub-contracts/scripts/deployUniswapAdapter.ts: -------------------------------------------------------------------------------- 1 | import { ethers } from "hardhat" 2 | import { config } from "@streamr/config" 3 | 4 | const { log } = console 5 | 6 | const { 7 | CHAIN = 'dev1', 8 | } = process.env 9 | 10 | const { 11 | contracts: { 12 | MarketplaceV3: MARKETPLACE_ADDRESS, 13 | } 14 | } = (config as any)[CHAIN] 15 | 16 | if (!MARKETPLACE_ADDRESS) { throw new Error(`No MarketplaceV3 found in chain "${CHAIN}"`) } 17 | 18 | /** 19 | * npx hardhat run --network dev scripts/deployUniswapAdapter.ts 20 | */ 21 | async function main(network: string) { 22 | let uniswapV2RouterAddress: string 23 | 24 | switch (network) { 25 | case 'matic': { 26 | uniswapV2RouterAddress = '0xa5E0829CaCEd8fFDD4De3c43696c57F7D7A678ff' 27 | break 28 | } 29 | case 'xdai': { 30 | uniswapV2RouterAddress = '0x1C232F01118CB8B424793ae03F870aa7D0ac7f77' 31 | break 32 | } 33 | case 'dev': { 34 | uniswapV2RouterAddress = '0xeE1bC9a7BFF1fFD913f4c97B6177D47E804E1920' // local docker dev mainchain 35 | break 36 | } 37 | default: { 38 | uniswapV2RouterAddress = '0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D' // Ethereum mainnet, Ropsten, Rinkeby, Görli, Kovan 39 | } 40 | } 41 | 42 | const Uniswap2Adapter = await ethers.getContractFactory("Uniswap2Adapter") 43 | const uniswap2Adapter = await Uniswap2Adapter.deploy(MARKETPLACE_ADDRESS, uniswapV2RouterAddress) 44 | await uniswap2Adapter.deployed() 45 | log(`Uniswap2Adapter deployed at ${uniswap2Adapter.address}`) 46 | } 47 | 48 | main("dev").catch((error) => { 49 | console.error(error) 50 | process.exitCode = 1 51 | }) 52 | -------------------------------------------------------------------------------- /packages/hub-contracts/scripts/hubEnvDeployer.ts: -------------------------------------------------------------------------------- 1 | import { HubEnvDeployer } from "../src/HubEnvDeployer" 2 | 3 | import { config } from "@streamr/config" 4 | const { 5 | contracts: { 6 | StreamRegistry, 7 | DATA, 8 | } 9 | } = config.dev1 10 | 11 | async function main() { 12 | // sidechain key preloaded with ETH (from docker-dev-init) 13 | const key = "0x2cd9855d17e01ce041953829398af7e48b24ece04ff9d0e183414de54dc52285" 14 | const url = "http://10.200.10.1:8546" 15 | // const streamRegistryAddress = "0x6cCdd5d866ea766f6DF5965aA98DeCCD629ff222" 16 | const destinationDomainId = 8997 17 | 18 | const hubEnvDeployer = new HubEnvDeployer( 19 | key, 20 | url, 21 | StreamRegistry, 22 | destinationDomainId 23 | ) 24 | 25 | await hubEnvDeployer.deployCoreContracts(DATA) 26 | } 27 | 28 | main() 29 | -------------------------------------------------------------------------------- /packages/hub-contracts/scripts/interactStreamRegistry.ts: -------------------------------------------------------------------------------- 1 | import { ethers as hardhatEthers } from "hardhat" 2 | import { config } from "@streamr/config" 3 | import { ProjectRegistryV1, StreamRegistryV5 } from "../typechain" 4 | 5 | const { getContractFactory } = hardhatEthers 6 | 7 | // export const log = (..._: unknown[]): void => { /* skip logging */ } 8 | const { log } = console 9 | 10 | const { 11 | CHAIN = 'polygon', 12 | } = process.env 13 | 14 | const { 15 | contracts: { 16 | StreamRegistry: STREAM_REGISTRY_ADDRESS, 17 | ProjectRegistry: PROJECT_REGISTRY_ADDRESS, 18 | } 19 | } = (config as any)[CHAIN] 20 | 21 | let projectRegistry: ProjectRegistryV1 22 | let streamRegistry: StreamRegistryV5 23 | 24 | const connectContracts = async () => { 25 | const projectRegistryFactory = await getContractFactory("ProjectRegistryV1") 26 | const projectRegistryFactoryTx = await projectRegistryFactory.attach(PROJECT_REGISTRY_ADDRESS) 27 | projectRegistry = await projectRegistryFactoryTx.deployed() as ProjectRegistryV1 28 | log("ProjectRegistryV1 deployed at: ", projectRegistry.address) 29 | 30 | const streamRegistryFactory = await getContractFactory("StreamRegistryV5") 31 | const streamRegistryFactoryTx = await streamRegistryFactory.attach(STREAM_REGISTRY_ADDRESS) 32 | streamRegistry = await streamRegistryFactoryTx.deployed() as StreamRegistryV5 33 | log("StreamRegistryV5 deployed at: ", streamRegistry.address) 34 | 35 | const latestBlock = await hardhatEthers.provider.getBlock("latest") 36 | log('latestBlock', latestBlock.number) 37 | } 38 | 39 | const grantTrustedRoleToProjectRegistry = async (): Promise => { 40 | log('Granting trusted role...') 41 | const trustedRole = await streamRegistry.TRUSTED_ROLE() 42 | const tx = await streamRegistry.grantRole(trustedRole, projectRegistry.address) 43 | log('tx', tx.hash) 44 | await tx.wait() 45 | log('StreamRegistry granted trusted role to ProjectRegistryV1') 46 | } 47 | 48 | /** 49 | * npx hardhat run --network polygon scripts/interactStreamRegistry.ts 50 | */ 51 | async function main() { 52 | await connectContracts() 53 | 54 | await grantTrustedRoleToProjectRegistry() 55 | } 56 | 57 | main().catch((error) => { 58 | console.error(error) 59 | process.exitCode = 1 60 | }) 61 | -------------------------------------------------------------------------------- /packages/hub-contracts/scripts/upgradeMarketplaceV3.ts: -------------------------------------------------------------------------------- 1 | import { ethers, upgrades } from "hardhat" 2 | import { config } from "@streamr/config" 3 | 4 | const { log } = console 5 | 6 | const { 7 | CHAIN, 8 | } = process.env 9 | 10 | if (!CHAIN) { throw new Error("Please specify CHAIN environment variable (dev0, dev1, gnosis, polygon, mainnet)") } 11 | 12 | const { 13 | contracts: { 14 | MarketplaceV3: MARKETPLACE_V3_ADDRESS 15 | } 16 | } = (config as any)[CHAIN] 17 | 18 | if (!MARKETPLACE_V3_ADDRESS) { throw new Error(`No MarketplaceV3 found in chain "${CHAIN}"`) } 19 | 20 | // 2022-11-21: deploying a bugfix to MarketplaceV3, adding ERC677 transferAndCall to outgoing token transfer (to product beneficiary) 21 | 22 | /** 23 | * npx hardhat run --network $CHAIN scripts/upgradeMarketplaceV3.ts 24 | */ 25 | async function main() { 26 | const MarketplaceV3Factory = await ethers.getContractFactory("MarketplaceV3") 27 | const marketplace = await upgrades.upgradeProxy(MARKETPLACE_V3_ADDRESS, MarketplaceV3Factory) 28 | await marketplace.deployed() 29 | log(`Upgraded MarketplaceV3 at ${marketplace.address}`) 30 | } 31 | 32 | main().catch((error) => { 33 | console.error(error) 34 | process.exitCode = 1 35 | }) 36 | -------------------------------------------------------------------------------- /packages/hub-contracts/scripts/upgradeProjectRegistry.ts: -------------------------------------------------------------------------------- 1 | import { ethers, upgrades } from "hardhat" 2 | import { config } from "@streamr/config" 3 | 4 | const { log } = console 5 | 6 | const { 7 | CHAIN = "dev1", 8 | } = process.env 9 | 10 | if (!CHAIN) { throw new Error("Please specify CHAIN environment variable (dev0, dev1, gnosis, polygon, mainnet)") } 11 | 12 | const { 13 | contracts: { 14 | ProjectRegistryV1: PROXY_ADDRESS 15 | } 16 | } = (config as any)[CHAIN] 17 | 18 | if (!PROXY_ADDRESS) { throw new Error(`No ProjectRegistryV1 found in chain "${CHAIN}"`) } 19 | 20 | /** 21 | * npx hardhat run --network polygon scripts/upgradeProjectRegistry.ts 22 | * npx hardhat flatten contracts/ProjectRegistry/ProjectRegistryV1.sol > pr.sol 23 | */ 24 | async function main() { 25 | log(`Upgrading ProjectRegistryV1 on ${CHAIN} chain at address ${PROXY_ADDRESS}...`) 26 | 27 | const ProjectRegistryFactory = await ethers.getContractFactory("ProjectRegistryV1") 28 | const projectRegistry = await upgrades.upgradeProxy(PROXY_ADDRESS, ProjectRegistryFactory) 29 | await projectRegistry.deployed() 30 | log(`Upgraded ProjectRegistryV1 at ${projectRegistry.address}`) 31 | } 32 | 33 | main().catch((error) => { 34 | console.error(error) 35 | process.exitCode = 1 36 | }) 37 | -------------------------------------------------------------------------------- /packages/hub-contracts/scripts/upgradeProjectStaking.ts: -------------------------------------------------------------------------------- 1 | import { ethers, upgrades } from "hardhat" 2 | import { config } from "@streamr/config" 3 | 4 | const { log } = console 5 | 6 | const { 7 | CHAIN = "dev1", 8 | } = process.env 9 | 10 | if (!CHAIN) { throw new Error("Please specify CHAIN environment variable (dev0, dev1, gnosis, polygon, mainnet)") } 11 | 12 | const { 13 | contracts: { 14 | ProjectStakingV1: PROXY_ADDRESS 15 | } 16 | } = (config as any)[CHAIN] 17 | 18 | if (!PROXY_ADDRESS) { throw new Error(`No ProjectStakingV1 found in chain "${CHAIN}"`) } 19 | 20 | /** 21 | * npx hardhat run --network polygon scripts/upgradeProjectStaking.ts 22 | * npx hardhat flatten contracts/ProjectStaking/ProjectStakingV1.sol > ps.sol 23 | */ 24 | async function main() { 25 | log(`Upgrading ProjectStakingV1 on ${CHAIN} chain at address ${PROXY_ADDRESS}...`) 26 | 27 | const ProjectStakingFactory = await ethers.getContractFactory("ProjectStakingV1") 28 | const projectStaking = await upgrades.upgradeProxy(PROXY_ADDRESS, ProjectStakingFactory) 29 | await projectStaking.deployed() 30 | log(`Upgraded ProjectStakingV1 at ${projectStaking.address}`) 31 | } 32 | 33 | main().catch((error) => { 34 | console.error(error) 35 | process.exitCode = 1 36 | }) 37 | -------------------------------------------------------------------------------- /packages/hub-contracts/scripts/upgradeRemoteMarketplace.ts: -------------------------------------------------------------------------------- 1 | import { ethers, upgrades } from "hardhat" 2 | import { config } from "@streamr/config" 3 | 4 | const { log } = console 5 | 6 | const { 7 | CHAIN = "gnosis", 8 | } = process.env 9 | 10 | if (!CHAIN) { throw new Error("Please specify CHAIN environment variable (dev0, dev1, gnosis, polygon, mainnet)") } 11 | 12 | const { 13 | contracts: { 14 | RemoteMarketplaceV1: PROXY_ADDRESS 15 | } 16 | } = (config as any)[CHAIN] 17 | 18 | if (!PROXY_ADDRESS) { throw new Error(`No RemoteMarketplaceV1 found in chain "${CHAIN}"`) } 19 | 20 | /** 21 | * npx hardhat run --network gnosis scripts/upgradeRemoteMarketplace.ts 22 | * npx hardhat flatten contracts/Marketplace/RemoteMarketplaceV1.sol > rm.sol 23 | */ 24 | async function main() { 25 | log(`Upgrading RemoteMarketplaceV1 on ${CHAIN} chain at address ${PROXY_ADDRESS}...`) 26 | 27 | const RemoteMarketplaceFactory = await ethers.getContractFactory("RemoteMarketplaceV1") 28 | const remoteMarketplace = await upgrades.upgradeProxy(PROXY_ADDRESS, RemoteMarketplaceFactory) 29 | await remoteMarketplace.deployed() 30 | log(`Upgraded RemoteMarketplaceV1 at ${remoteMarketplace.address}`) 31 | } 32 | 33 | main().catch((error) => { 34 | console.error(error) 35 | process.exitCode = 1 36 | }) 37 | -------------------------------------------------------------------------------- /packages/hub-contracts/src/contracts/enums.ts: -------------------------------------------------------------------------------- 1 | // TODO: where should enums be so they'd stay synced automagically? 2 | export const ProductState = { 3 | NotDeployed: 0, // non-existent or deleted 4 | Deployed: 1, // created or redeployed 5 | } 6 | export const productStateName = Object.getOwnPropertyNames(ProductState) 7 | -------------------------------------------------------------------------------- /packages/hub-contracts/src/exports.ts: -------------------------------------------------------------------------------- 1 | export { abi as projectRegistryV1ABI, bytecode as projectRegistryV1Bytecode } 2 | from "../artifacts/contracts/ProjectRegistry/ProjectRegistryV1.sol/ProjectRegistryV1.json" 3 | export type { ProjectRegistryV1 } from "../typechain/contracts/ProjectRegistry/ProjectRegistryV1.sol/ProjectRegistryV1" 4 | 5 | export { abi as projectStakingV1ABI, bytecode as projectStakingV1Bytecode } 6 | from "../artifacts/contracts/ProjectStaking/ProjectStakingV1.sol/ProjectStakingV1.json" 7 | export type { ProjectStakingV1 } from "../typechain/contracts/ProjectStaking/ProjectStakingV1" 8 | 9 | export { abi as marketplaceV4ABI, bytecode as marketplaceV4Bytecode } 10 | from "../artifacts/contracts/Marketplace/MarketplaceV4.sol/MarketplaceV4.json" 11 | export type { MarketplaceV4 } from "../typechain/contracts/Marketplace/MarketplaceV4.sol/MarketplaceV4" 12 | 13 | export { abi as uniswap2AdapterV4ABI, bytecode as uniswap2AdapterV4Bytecode } 14 | from "../artifacts/contracts/Marketplace/Uniswap2AdapterV4.sol/Uniswap2AdapterV4.json" 15 | export type { Uniswap2AdapterV4 } from "../typechain/contracts/Marketplace/Uniswap2AdapterV4.sol/Uniswap2AdapterV4" 16 | 17 | export { abi as remoteMarketplaceV1ABI, bytecode as remoteMarketplaceV1Bytecode } 18 | from "../artifacts/contracts/Marketplace/RemoteMarketplaceV1.sol/RemoteMarketplaceV1.json" 19 | export type { RemoteMarketplaceV1 } from "../typechain/contracts/Marketplace/RemoteMarketplaceV1" 20 | 21 | export * from "./HubEnvDeployer" 22 | -------------------------------------------------------------------------------- /packages/hub-contracts/src/startWatcher.ts: -------------------------------------------------------------------------------- 1 | import { IMarketplace } from "../typechain/IMarketplace" 2 | import IMarketplaceJson from "../artifacts/contracts/IMarketplace.sol/IMarketplace.json" 3 | 4 | import { config } from "@streamr/config" 5 | import { Contract, providers } from "ethers" 6 | 7 | const { log } = console 8 | 9 | const { 10 | ethereum: { 11 | rpcEndpoints: [{ 12 | url: rpcUrl, 13 | }], 14 | contracts: { 15 | "Marketplace": marketplaceAddress 16 | } 17 | } 18 | } = config 19 | 20 | const provider = new providers.JsonRpcProvider(rpcUrl) 21 | 22 | async function start() { 23 | const market = new Contract(marketplaceAddress, IMarketplaceJson.abi, provider) as IMarketplace 24 | const productId = "0x0000000000000000000000000000000000000000000000000000000000000001" 25 | market.on(market.filters.Subscribed(productId), (productId, subscriber, endTimestamp) => { 26 | log("Found subscription to %s by %s ending at %s", productId, subscriber, endTimestamp.toString()) 27 | }) 28 | } 29 | start().catch(console.error) 30 | -------------------------------------------------------------------------------- /packages/hub-contracts/test/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | globals: { 3 | // mocha 4 | describe: "readonly", 5 | it: "readonly", 6 | before: "readonly", 7 | beforeEach: "readonly", 8 | after: "readonly", 9 | afterEach: "readonly", 10 | }, 11 | rules: { 12 | "no-console": "warn", 13 | }, 14 | } -------------------------------------------------------------------------------- /packages/hub-contracts/test/constants.ts: -------------------------------------------------------------------------------- 1 | export const types = { 2 | EIP712Domain: [ 3 | { 4 | name: 'name', type: 'string' 5 | }, 6 | { 7 | name: 'version', type: 'string' 8 | }, 9 | { 10 | name: 'chainId', type: 'uint256' 11 | }, 12 | { 13 | name: 'verifyingContract', type: 'address' 14 | }, 15 | ], 16 | ForwardRequest: [ 17 | { 18 | name: 'from', type: 'address' 19 | }, 20 | { 21 | name: 'to', type: 'address' 22 | }, 23 | { 24 | name: 'value', type: 'uint256' 25 | }, 26 | { 27 | name: 'gas', type: 'uint256' 28 | }, 29 | { 30 | name: 'nonce', type: 'uint256' 31 | }, 32 | { 33 | name: 'data', type: 'bytes' 34 | }, 35 | ], 36 | } -------------------------------------------------------------------------------- /packages/hub-contracts/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "display": "Node 12", 4 | "compilerOptions": { 5 | "lib": ["es2019", "es2020.promise", "es2020.bigint", "es2020.string"], 6 | "module": "CommonJS", 7 | "target": "es2019", 8 | "preserveSymlinks": true, 9 | "strict": true, 10 | "esModuleInterop": true, 11 | "skipLibCheck": true, 12 | "forceConsistentCasingInFileNames": true, 13 | "resolveJsonModule": true, 14 | "declaration": true, 15 | "outDir": "dist" 16 | }, 17 | "include": ["./src/exports.ts", "./src/HubEnvDeployer.ts"] 18 | } -------------------------------------------------------------------------------- /packages/hub-contracts/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "display": "Node 12", 4 | "compilerOptions": { 5 | "lib": ["es2019", "es2020.promise", "es2020.bigint", "es2020.string"], 6 | "module": "CommonJS", 7 | "target": "es2019", 8 | "preserveSymlinks": true, 9 | "strict": true, 10 | "esModuleInterop": true, 11 | "skipLibCheck": true, 12 | "forceConsistentCasingInFileNames": true, 13 | "resolveJsonModule": true, 14 | "moduleResolution": "node", 15 | "outDir": "dist" 16 | }, 17 | "include": [ 18 | "./scripts", 19 | "./test", 20 | "./typechain" 21 | ], 22 | "files": [ 23 | "./hardhat.config.ts" 24 | ] 25 | } -------------------------------------------------------------------------------- /packages/network-contracts/.eslintignore: -------------------------------------------------------------------------------- 1 | typechain 2 | -------------------------------------------------------------------------------- /packages/network-contracts/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "semi": ["error", "never"], 4 | "quotes": ["error", "double"] 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /packages/network-contracts/.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules/ 2 | /coverage/ 3 | /coverage.json 4 | /artifacts/ 5 | /deployments/ 6 | /typechain/ 7 | /cache/ 8 | /localConfig.json 9 | streamr-network-contracts-*.tgz 10 | codehashes.json 11 | -------------------------------------------------------------------------------- /packages/network-contracts/.solhint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "solhint:recommended", 3 | "plugins": [], 4 | "rules": { 5 | "max-states-count": "off", 6 | "code-complexity": [ 7 | "warn", 8 | 11 9 | ], 10 | "function-max-lines": [ 11 | "warn", 12 | 100 13 | ], 14 | "no-empty-blocks": "off", 15 | "no-unused-vars": "error", 16 | "payable-fallback": "off", 17 | "reason-string": [ 18 | "error", 19 | { 20 | "maxLength": 32 21 | } 22 | ], 23 | "no-global-import": "off", 24 | "constructor-syntax": "error", 25 | "const-name-snakecase": "error", 26 | "event-name-camelcase": "error", 27 | "func-name-mixedcase": "error", 28 | "func-param-name-mixedcase": "error", 29 | "modifier-name-mixedcase": "error", 30 | "use-forbidden-name": "error", 31 | "var-name-mixedcase": "error", 32 | "imports-on-top": "error", 33 | "visibility-modifier-order": "error", 34 | "avoid-call-value": "error", 35 | "avoid-low-level-calls": "warn", 36 | "avoid-sha3": "error", 37 | "avoid-suicide": "error", 38 | "avoid-throw": "error", 39 | "avoid-tx-origin": "error", 40 | "check-send-result": "error", 41 | "multiple-sends": "error", 42 | "no-complex-fallback": "error", 43 | "no-inline-assembly": "warn", 44 | "not-rely-on-block-hash": "error", 45 | "not-rely-on-time": "warn", 46 | "reentrancy": "error", 47 | "state-visibility": "error", 48 | "func-visibility": [ 49 | "error", 50 | { 51 | "ignoreConstructors": true 52 | } 53 | ], 54 | "quotes": [ 55 | "error", 56 | "double" 57 | ], 58 | "max-line-length": [ 59 | "error", 60 | 200 61 | ], 62 | "compiler-version": [ 63 | "error", 64 | ">=0.6.0" 65 | ] 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /packages/network-contracts/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "solidity.compileUsingRemoteVersion": "v0.8.13+commit.abaa5c0e", 3 | "solidity.packageDefaultDependenciesDirectory": "../../node_modules", 4 | "solidity.defaultCompiler": "remote" 5 | } -------------------------------------------------------------------------------- /packages/network-contracts/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -ex 3 | 4 | rm -rf artifacts 5 | npm run compile 6 | 7 | rm -rf dist 8 | tsc -p tsconfig.build.json 9 | 10 | # this requires a running Docker daemon 11 | if docker ps > /dev/null 2>&1; then 12 | ./generateSelectorsTxt.sh 13 | else 14 | echo "Docker is not running, skipping generateSelectorsTxt.sh" 15 | fi 16 | -------------------------------------------------------------------------------- /packages/network-contracts/contracts/GSN/AcceptEverythingPaymaster.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | pragma experimental ABIEncoderV2; 4 | 5 | import "@opengsn/contracts/src/BasePaymaster.sol"; 6 | 7 | // accept everything. 8 | // this paymaster accepts any request. 9 | // 10 | // NOTE: Do NOT use this contract on a mainnet: it accepts anything, so anyone can "grief" it and drain its account 11 | 12 | contract AcceptEverythingPaymaster is BasePaymaster { 13 | 14 | function versionPaymaster() external view override virtual returns (string memory){ 15 | return "2.2.3+opengsn.accepteverything.ipaymaster"; 16 | } 17 | 18 | function preRelayedCall( 19 | GsnTypes.RelayRequest calldata relayRequest, 20 | bytes calldata signature, 21 | bytes calldata approvalData, 22 | uint256 maxPossibleGas 23 | ) 24 | external 25 | override 26 | virtual 27 | returns (bytes memory context, bool revertOnRecipientRevert) { 28 | (relayRequest, signature, approvalData, maxPossibleGas); 29 | return ("", false); 30 | } 31 | 32 | function postRelayedCall( 33 | bytes calldata context, 34 | bool success, 35 | uint256 gasUseWithoutPost, 36 | GsnTypes.RelayData calldata relayData 37 | ) external override virtual { 38 | (context, success, gasUseWithoutPost, relayData); 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /packages/network-contracts/contracts/GSN/WhitelistPaymaster.sol: -------------------------------------------------------------------------------- 1 | /** 2 | * Deployed on polygon at 0x43E69adABC664617EB9C5E19413a335e9cd4A243 on Sep-27-2022 02:10:59 PM +UTC 3 | */ 4 | 5 | //SPDX-License-Identifier: MIT 6 | pragma solidity ^0.8.0; 7 | pragma experimental ABIEncoderV2; 8 | 9 | import "./AcceptEverythingPaymaster.sol"; 10 | 11 | ///a sample paymaster that has whitelists for senders and targets. 12 | /// - if at least one sender is whitelisted, then ONLY whitelisted senders are allowed. 13 | /// - if at least one target is whitelisted, then ONLY whitelisted targets are allowed. 14 | contract WhitelistPaymaster is AcceptEverythingPaymaster { 15 | 16 | bool public useSenderWhitelist; 17 | bool public useTargetWhitelist; 18 | mapping (address=>bool) public senderWhitelist; 19 | mapping (address=>bool) public targetWhitelist; 20 | 21 | function whitelistSender(address sender) public onlyOwner { 22 | senderWhitelist[sender]=true; 23 | useSenderWhitelist = true; 24 | } 25 | function whitelistTarget(address target) public onlyOwner { 26 | targetWhitelist[target]=true; 27 | useTargetWhitelist = true; 28 | } 29 | 30 | function preRelayedCall( 31 | GsnTypes.RelayRequest calldata relayRequest, 32 | bytes calldata signature, 33 | bytes calldata approvalData, 34 | uint256 maxPossibleGas 35 | ) 36 | external 37 | override 38 | virtual 39 | returns (bytes memory context, bool revertOnRecipientRevert) { 40 | (signature, maxPossibleGas); 41 | require(approvalData.length == 0, "approvalData: invalid length"); 42 | require(relayRequest.relayData.paymasterData.length == 0, "paymasterData: invalid length"); 43 | 44 | if ( useSenderWhitelist ) { 45 | require( senderWhitelist[relayRequest.request.from], "sender not whitelisted"); 46 | } 47 | if ( useTargetWhitelist ) { 48 | require( targetWhitelist[relayRequest.request.to], "target not whitelisted"); 49 | } 50 | return ("", false); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /packages/network-contracts/contracts/NodeRegistry/ERC20Mintable.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.9; 3 | import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; 4 | 5 | /** 6 | * ERC20Mintable is missing from open-zeppelin 3.0 7 | * See https://forum.openzeppelin.com/t/where-is-erc20mintable-sol-in-openzeppelin-contracts-3-0/2283 8 | */ 9 | contract ERC20Mintable is ERC20 { 10 | address private creator; 11 | constructor(string memory name, string memory symbol) ERC20(name, symbol) { 12 | creator = msg.sender; 13 | } 14 | 15 | function mint(address receipient, uint amount) public { 16 | require(msg.sender == creator, "only_creator"); 17 | _mint(receipient, amount); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /packages/network-contracts/contracts/NodeRegistry/NetworkParameters.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.9; 3 | 4 | import "./Ownable.sol"; 5 | 6 | contract NetworkParameters is Ownable { 7 | uint public minControlLayerVersion; 8 | uint public minMessageLayerVersion; 9 | string public minNetworkReferenceCodeVersion; 10 | address public tokenAddress; 11 | 12 | constructor(address owner, uint minControlLayerVersion_, uint minMessageLayerVersion_, string memory minNetworkReferenceCodeVersion_, address tokenAddress_) Ownable(owner) { 13 | minControlLayerVersion = minControlLayerVersion_; 14 | minMessageLayerVersion = minMessageLayerVersion_; 15 | minNetworkReferenceCodeVersion = minNetworkReferenceCodeVersion_; 16 | tokenAddress = tokenAddress_; 17 | } 18 | 19 | function setMinControlLayerVersion(uint version) public onlyOwner { 20 | minControlLayerVersion = version; 21 | } 22 | 23 | function setMinMessageLayerVersion(uint version) public onlyOwner { 24 | minControlLayerVersion = version; 25 | } 26 | 27 | function setMinNetworkReferenceCodeVersion(string memory version) public onlyOwner { 28 | minNetworkReferenceCodeVersion = version; 29 | } 30 | 31 | function setTokenAddress(address tokenAddress_) public onlyOwner { 32 | tokenAddress = tokenAddress_; 33 | } 34 | } -------------------------------------------------------------------------------- /packages/network-contracts/contracts/NodeRegistry/NodeDomainNameHelper.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.9; 3 | 4 | contract NodeDomainNameHelper { 5 | event Request(address indexed requestor, string indexed ipAddress, uint port); 6 | 7 | function request(string memory ipAddress, uint port) public { 8 | emit Request(msg.sender, ipAddress, port); 9 | } 10 | } -------------------------------------------------------------------------------- /packages/network-contracts/contracts/NodeRegistry/Ownable.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.9; 3 | 4 | /** 5 | * @title Ownable 6 | * @dev The Ownable contract has an owner address, and provides basic authorization control 7 | * functions, this simplifies the implementation of "user permissions". 8 | */ 9 | contract Ownable { 10 | address public owner; 11 | address public pendingOwner; 12 | 13 | event OwnershipTransferred( 14 | address indexed previousOwner, 15 | address indexed newOwner 16 | ); 17 | 18 | /** 19 | * @dev The Ownable constructor sets the original `owner` of the contract to the sender 20 | * account. 21 | */ 22 | constructor(address owner_) { 23 | owner = owner_; 24 | } 25 | 26 | /** 27 | * @dev Throws if called by any account other than the owner. 28 | */ 29 | modifier onlyOwner() { 30 | require(msg.sender == owner, "onlyOwner"); 31 | _; 32 | } 33 | 34 | /** 35 | * @dev Allows the current owner to set the pendingOwner address. 36 | * @param newOwner The address to transfer ownership to. 37 | */ 38 | function transferOwnership(address newOwner) public onlyOwner { 39 | pendingOwner = newOwner; 40 | } 41 | 42 | /** 43 | * @dev Allows the pendingOwner address to finalize the transfer. 44 | */ 45 | function claimOwnership() public { 46 | require(msg.sender == pendingOwner, "onlyPendingOwner"); 47 | emit OwnershipTransferred(owner, pendingOwner); 48 | owner = pendingOwner; 49 | pendingOwner = address(0); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /packages/network-contracts/contracts/NodeRegistry/TokenBalanceWeightStrategy.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.9; 3 | 4 | import "./WeightStrategy.sol"; 5 | import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; 6 | 7 | contract TokenBalanceWeightStrategy is WeightStrategy { 8 | ERC20 public token; 9 | 10 | constructor(address tokenAddress) { 11 | token = ERC20(tokenAddress); 12 | } 13 | 14 | function getWeight(address nodeAddress) public override view returns (uint) { 15 | return token.balanceOf(nodeAddress); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /packages/network-contracts/contracts/NodeRegistry/WeightStrategy.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.9; 3 | 4 | interface WeightStrategy { 5 | function getWeight(address nodeAddress) external view returns (uint); 6 | } -------------------------------------------------------------------------------- /packages/network-contracts/contracts/NodeRegistry/WeightedNodeRegistry.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.9; 3 | pragma experimental ABIEncoderV2; 4 | 5 | import "./WeightStrategy.sol"; 6 | import "./NodeRegistry.sol"; 7 | import "@openzeppelin/contracts-upgradeable-4.4.2/proxy/utils/UUPSUpgradeable.sol"; 8 | import "@openzeppelin/contracts-upgradeable-4.4.2/proxy/utils/Initializable.sol"; 9 | 10 | contract WeightedNodeRegistry is NodeRegistry { 11 | WeightStrategy public strat; 12 | 13 | // Constructor can't be used with upgradeable contracts, so use initialize instead 14 | // this will not be called upon each upgrade, only once during first deployment 15 | function initialize(address owner_, bool requiresWhitelist_, address weightStrategy_, address[] memory initialNodes, string[] memory initialUrls) public initializer { 16 | NodeRegistry.initialize(owner_, requiresWhitelist_, initialNodes, initialUrls); 17 | strat = WeightStrategy(weightStrategy_); 18 | } 19 | 20 | function getWeight(address nodeAddress) public view returns (uint) { 21 | return strat.getWeight(nodeAddress); 22 | } 23 | 24 | function setWeightStrategy(address weightStrategy_) public onlyOwner { 25 | strat = WeightStrategy(weightStrategy_); 26 | } 27 | } -------------------------------------------------------------------------------- /packages/network-contracts/contracts/OperatorTokenomics/IERC677.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.9; 4 | 5 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 6 | 7 | interface IERC677 is IERC20 { 8 | function transferAndCall( 9 | address to, 10 | uint value, 11 | bytes calldata data 12 | ) external returns (bool success); 13 | 14 | // renamed to avoid `Duplicate definition of Transfer (Transfer(address,address,uint256,bytes), Transfer(address,address,uint256))` 15 | event TransferAndCall( 16 | address indexed from, 17 | address indexed to, 18 | uint value, 19 | bytes data 20 | ); 21 | } 22 | -------------------------------------------------------------------------------- /packages/network-contracts/contracts/OperatorTokenomics/IERC677Receiver.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.9; 4 | 5 | interface IERC677Receiver { 6 | /** @dev onTokenTransfer implementation MUST start with check that `msg.sender == tokenAddress` */ 7 | function onTokenTransfer(address sender, uint256 value, bytes calldata data) external; 8 | } 9 | -------------------------------------------------------------------------------- /packages/network-contracts/contracts/OperatorTokenomics/IOperator.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | // callbacks supported by the Sponsorship, for operator smart contracts (Operator) 6 | interface IOperator { 7 | /** @param slashingWei how much was taken from the operator's stake */ 8 | function onSlash(uint slashingWei) external; 9 | 10 | /** @param payoutWei how much the Operator received from the forced withdraw+unstaking (minus slashing or forfeited stake) */ 11 | function onKick(uint slashingWei, uint payoutWei) external; 12 | } 13 | -------------------------------------------------------------------------------- /packages/network-contracts/contracts/OperatorTokenomics/IRandomOracle.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | interface IRandomOracle { 6 | function getRandomBytes32() external returns (bytes32); 7 | } 8 | -------------------------------------------------------------------------------- /packages/network-contracts/contracts/OperatorTokenomics/IVoterRegistry.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | // Operator contract announces to the registry when it's eligible to vote, or not eligible anymore. 6 | interface IVoterRegistry { 7 | event VoterUpdate(address indexed voterAddress, bool indexed isVoter); 8 | 9 | /** Will check if the given contract is eligible or not, update voter list if needed */ 10 | function voterUpdate(address operatorContractAddress) external returns (bool isEligible); 11 | 12 | function voterCount() external view returns (uint); 13 | function voters(uint index) external view returns (address); 14 | } 15 | -------------------------------------------------------------------------------- /packages/network-contracts/contracts/OperatorTokenomics/OperatorPolicies/DefaultDelegationPolicy.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.13; 4 | 5 | import "./IDelegationPolicy.sol"; 6 | import "../StreamrConfig.sol"; 7 | import "../Operator.sol"; 8 | 9 | contract DefaultDelegationPolicy is IDelegationPolicy, Operator { 10 | 11 | function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { 12 | return interfaceId == type(IDelegationPolicy).interfaceId; 13 | } 14 | 15 | function setParam(uint param) external { 16 | 17 | } 18 | 19 | /** 20 | * Check the operator's self-delegation fraction i.e. how much of the Operator token supply does the operator have as "skin in the game" 21 | * @dev Consequences of the minimum-self-delegation rule: 22 | * @dev - the first delegation must be self-delegation since at first balance(owner) == 0 23 | * @dev - if minimumSelfDelegationFraction == 0, then any delegations are fine, AS LONG AS the owner has some tokens 24 | */ 25 | // solc-ignore-next-line func-mutability 26 | function onDelegate(address) external { 27 | // multiplying the left side by 1 ether is equivalent to dividing the right side by 1 ether, but numerically a lot better 28 | require(1 ether * balanceOf(owner) > totalSupply() * streamrConfig.minimumSelfDelegationFraction(), "error_selfDelegationTooLow"); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /packages/network-contracts/contracts/OperatorTokenomics/OperatorPolicies/IDelegationPolicy.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.13; 4 | 5 | interface IDelegationPolicy { 6 | function setParam(uint param) external; 7 | 8 | /** can throw to prevent delegation. Gets called AFTER the operator token minting */ 9 | function onDelegate(address delegator) external; 10 | } 11 | -------------------------------------------------------------------------------- /packages/network-contracts/contracts/OperatorTokenomics/OperatorPolicies/INodeModule.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.13; 4 | 5 | interface INodeModule { 6 | function createCoordinationStream() external; 7 | function _setNodeAddresses(address[] calldata newNodes) external; 8 | function _updateNodeAddresses(address[] calldata addNodes, address[] calldata removeNodes) external; 9 | 10 | } -------------------------------------------------------------------------------- /packages/network-contracts/contracts/OperatorTokenomics/OperatorPolicies/IQueueModule.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.13; 4 | 5 | import "../Sponsorship.sol"; 6 | 7 | interface IQueueModule { 8 | function _undelegate(uint amountWei, address undelegator) external; 9 | function _payOutQueue(uint maxIterations) external; 10 | function _payOutFirstInQueue() external returns (uint payoutComplete); 11 | function _triggerAnotherOperatorWithdraw(address other, Sponsorship[] memory sponsorshipAddresses) external; 12 | } -------------------------------------------------------------------------------- /packages/network-contracts/contracts/OperatorTokenomics/OperatorPolicies/IStakeModule.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.13; 4 | 5 | import "../Sponsorship.sol"; 6 | 7 | interface IStakeModule { 8 | function _stake(Sponsorship sponsorship, uint amountWei) external; 9 | function _reduceStakeTo(Sponsorship sponsorship, uint targetStakeWei) external; 10 | function _unstake(Sponsorship sponsorship) external; 11 | function _forceUnstake(Sponsorship sponsorship) external; 12 | function _removeSponsorship(Sponsorship sponsorship, uint receivedDuringUnstakingWei) external; 13 | function _withdrawEarnings(Sponsorship[] memory sponsorshipAddresses) external returns (uint sumEarnings); 14 | } 15 | -------------------------------------------------------------------------------- /packages/network-contracts/contracts/OperatorTokenomics/OperatorPolicies/IUndelegationPolicy.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.13; 4 | 5 | interface IUndelegationPolicy { 6 | function setParam(uint param) external; 7 | 8 | /** can throw to prevent undelegation. Gets called BEFORE entering the undelegation queue */ 9 | function onUndelegate(address delegator, uint amount) external; 10 | } 11 | -------------------------------------------------------------------------------- /packages/network-contracts/contracts/OperatorTokenomics/SponsorshipPolicies/AdminKickPolicy.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.13; 4 | 5 | import "./IKickPolicy.sol"; 6 | import "../Sponsorship.sol"; 7 | 8 | contract AdminKickPolicy is IKickPolicy, Sponsorship { 9 | struct LocalStorage { 10 | address admin; 11 | } 12 | 13 | function localData() internal view returns(LocalStorage storage data) { 14 | bytes32 storagePosition = keccak256(abi.encodePacked("sponsorship.storage.AdminKickPolicy", address(this))); 15 | assembly {data.slot := storagePosition} // solhint-disable-line no-inline-assembly 16 | } 17 | 18 | function setParam(uint adminAdress) external { 19 | localData().admin = address(uint160(adminAdress)); 20 | } 21 | 22 | function onFlag(address operator, address flagger) external { 23 | require(localData().admin == flagger, "error_onlyAdmin"); 24 | _kick(operator, 0); 25 | } 26 | 27 | function onVote(address, bytes32, address) external { 28 | } 29 | 30 | function getFlagData(address) override external pure returns (uint flagData) { 31 | return 0; 32 | } 33 | 34 | function getMinimumStakeOf(address) override external pure returns (uint individualMinimumStakeWei) { 35 | return 0; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /packages/network-contracts/contracts/OperatorTokenomics/SponsorshipPolicies/DefaultLeavePolicy.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.13; 4 | 5 | import "./ILeavePolicy.sol"; 6 | import "../Sponsorship.sol"; 7 | 8 | contract DefaultLeavePolicy is ILeavePolicy, Sponsorship { 9 | 10 | // TODO: use LocalStorage pattern for this 11 | uint public penaltyPeriodSeconds; // = 0 12 | 13 | function setParam(uint penaltyPeriod) external { 14 | require (penaltyPeriod <= streamrConfig.maxPenaltyPeriodSeconds(), "error_penaltyPeriodTooLong"); 15 | penaltyPeriodSeconds = penaltyPeriod; 16 | } 17 | 18 | /** 19 | * After penaltyPeriod, leaving is always okay 20 | * During penaltyPeriod, leaving is only okay if sponsorship is not paying for the service 21 | */ 22 | function getLeavePenaltyWei(address operator) public override view returns (uint leavePenaltyWei) { 23 | uint joinTimestamp = joinTimeOfOperator[operator]; 24 | if (block.timestamp >= joinTimestamp + penaltyPeriodSeconds) { // solhint-disable-line not-rely-on-time 25 | return 0; 26 | } 27 | 28 | if (isRunning() && isFunded()) { 29 | return streamrConfig.earlyLeaverPenaltyWei(); 30 | } 31 | return 0; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /packages/network-contracts/contracts/OperatorTokenomics/SponsorshipPolicies/IAllocationPolicy.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.13; 4 | 5 | interface IAllocationPolicy { 6 | function setParam(uint param) external; 7 | function getEarningsWei(address operator) external view returns (uint earningsWei); 8 | function getInsolvencyTimestamp() external view returns (uint insolvencyTimestamp); 9 | function onJoin(address operator) external; 10 | function onLeave(address operator) external; 11 | function onWithdraw(address operator) external returns (uint payoutWei); 12 | function onStakeChange(address operator, int stakeChangeWei) external; 13 | function onSponsor(address sponsor, uint amountWei) external; 14 | } 15 | -------------------------------------------------------------------------------- /packages/network-contracts/contracts/OperatorTokenomics/SponsorshipPolicies/IJoinPolicy.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.13; 4 | 5 | interface IJoinPolicy { 6 | function setParam(uint param) external; 7 | function onJoin(address operator, uint amount) external; 8 | } 9 | -------------------------------------------------------------------------------- /packages/network-contracts/contracts/OperatorTokenomics/SponsorshipPolicies/IKickPolicy.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.13; 4 | 5 | interface IKickPolicy { 6 | enum FlagState { 7 | NONE, 8 | VOTING, 9 | KICKED, 10 | NOT_KICKED 11 | } 12 | 13 | function setParam(uint param) external; 14 | function onFlag(address target, address flagger) external; 15 | function onVote(address operator, bytes32 voteData, address voter) external; 16 | function getFlagData(address operator) external view returns (uint flagData); 17 | function getMinimumStakeOf(address operator) external view returns (uint individualMinimumStakeWei); 18 | } 19 | -------------------------------------------------------------------------------- /packages/network-contracts/contracts/OperatorTokenomics/SponsorshipPolicies/ILeavePolicy.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.13; 4 | 5 | interface ILeavePolicy { 6 | function setParam(uint param) external; 7 | function getLeavePenaltyWei(address operator) external view returns (uint leavePenaltyWei); 8 | } 9 | -------------------------------------------------------------------------------- /packages/network-contracts/contracts/OperatorTokenomics/SponsorshipPolicies/MaxOperatorsJoinPolicy.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.13; 4 | 5 | import "./IJoinPolicy.sol"; 6 | import "../Sponsorship.sol"; 7 | 8 | 9 | contract MaxOperatorsJoinPolicy is IJoinPolicy, Sponsorship { 10 | struct LocalStorage { 11 | uint256 maxOperators; 12 | } 13 | 14 | function localData() internal view returns(LocalStorage storage data) { 15 | bytes32 storagePosition = keccak256(abi.encodePacked("sponsorship.storage.MaxOperatorsJoinPolicy", address(this))); 16 | assembly {data.slot := storagePosition} // solhint-disable-line no-inline-assembly 17 | } 18 | 19 | function setParam(uint256 maxOperators) external { 20 | localData().maxOperators = maxOperators; 21 | } 22 | 23 | // solc-ignore-next-line func-mutability 24 | function onJoin(address, uint256) external { 25 | require(operatorCount <= localData().maxOperators, "error_tooManyOperators"); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /packages/network-contracts/contracts/OperatorTokenomics/SponsorshipPolicies/OperatorContractOnlyJoinPolicy.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.13; 4 | 5 | import "./IJoinPolicy.sol"; 6 | import "../Sponsorship.sol"; 7 | 8 | interface IFactory { 9 | function deploymentTimestamp(address) external view returns (uint); // zero for contracts not deployed by this factory 10 | } 11 | 12 | /** 13 | * Only Operator contracts that were deployed using the official OperatorFactory are allowed to join 14 | */ 15 | contract OperatorContractOnlyJoinPolicy is IJoinPolicy, Sponsorship { 16 | 17 | function setParam(uint) external {} 18 | 19 | // solc-ignore-next-line func-mutability 20 | function onJoin(address operator, uint) external { 21 | require(IFactory(streamrConfig.operatorFactory()).deploymentTimestamp(operator) > 0, "error_onlyOperators"); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /packages/network-contracts/contracts/OperatorTokenomics/testcontracts/MockRandomOracle.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.13; 3 | 4 | import "../IRandomOracle.sol"; 5 | 6 | // use this file to generate selectors.txt => include "top of inheritance-chain contracts" here 7 | import "../SponsorshipFactory.sol"; 8 | import "../OperatorFactory.sol"; 9 | 10 | contract MockRandomOracle is IRandomOracle { 11 | bytes32[] public outcomes = [ bytes32(0x1234567812345678123456781234567812345678123456781234567812345678) ]; 12 | uint public index = 0; 13 | 14 | function getRandomBytes32() external returns (bytes32 outcome) { 15 | outcome = outcomes[index]; 16 | index = (index + 1) % outcomes.length; 17 | } 18 | 19 | function setOutcomes(bytes32[] calldata mockRandomBytes32List) external { 20 | require(outcomes.length > 0, "can't set empty outcomes array!"); 21 | outcomes = mockRandomBytes32List; 22 | index = 0; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /packages/network-contracts/contracts/OperatorTokenomics/testcontracts/TestExchangeRatePolicy.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.13; 4 | 5 | import "../OperatorPolicies/IExchangeRatePolicy.sol"; 6 | 7 | contract TestExchangeRatePolicy is IExchangeRatePolicy { 8 | 9 | function supportsInterface(bytes4 interfaceId) public pure returns (bool) { 10 | return interfaceId == type(IExchangeRatePolicy).interfaceId; 11 | } 12 | 13 | function setParam(uint) external {} 14 | 15 | function operatorTokenToData(uint) external view returns (uint) { 16 | // solhint-disable-next-line reason-string 17 | require(false); // using delegatecall the (success, data) returned values will be (0, 0) 18 | } 19 | 20 | function operatorTokenToDataInverse(uint) external view returns (uint) {} 21 | 22 | function dataToOperatorToken(uint dataWei, uint) external view returns (uint) { 23 | return dataWei; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /packages/network-contracts/contracts/OperatorTokenomics/testcontracts/TestExchangeRatePolicy2.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.13; 4 | 5 | import "../OperatorPolicies/IExchangeRatePolicy.sol"; 6 | 7 | contract TestExchangeRatePolicy2 is IExchangeRatePolicy { 8 | 9 | function supportsInterface(bytes4 interfaceId) public pure returns (bool) { 10 | return interfaceId == type(IExchangeRatePolicy).interfaceId; 11 | } 12 | 13 | function setParam(uint) external {} 14 | 15 | function operatorTokenToData(uint) external view returns (uint) { 16 | require(false, "revertedWithStringReason"); // using delegatecall the (success, data) returned values will be (0, 100) 17 | } 18 | 19 | function operatorTokenToDataInverse(uint dataWei) external view returns (uint) {} 20 | 21 | function dataToOperatorToken(uint dataWei, uint) external view returns (uint) { 22 | return dataWei; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /packages/network-contracts/contracts/OperatorTokenomics/testcontracts/TestExchangeRatePolicy3.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.13; 4 | 5 | import "../OperatorPolicies/IExchangeRatePolicy.sol"; 6 | 7 | contract TestExchangeRatePolicy3 is IExchangeRatePolicy { 8 | 9 | function supportsInterface(bytes4 interfaceId) public pure returns (bool) { 10 | return interfaceId == type(IExchangeRatePolicy).interfaceId; 11 | } 12 | 13 | function setParam(uint) external {} 14 | 15 | function operatorTokenToData(uint tokenWei) external view returns (uint) { 16 | return tokenWei; 17 | } 18 | 19 | function operatorTokenToDataInverse(uint dataWei) external view returns (uint) {} 20 | 21 | function dataToOperatorToken(uint, uint) external view returns (uint) { 22 | // solhint-disable-next-line reason-string 23 | require(false); // using delegatecall the (success, data) returned values will be (0, 0) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /packages/network-contracts/contracts/OperatorTokenomics/testcontracts/TestJoinPolicy.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | /* solhint-disable reason-string */ 3 | 4 | pragma solidity ^0.8.13; 5 | 6 | import "../SponsorshipPolicies/IJoinPolicy.sol"; 7 | import "../Sponsorship.sol"; 8 | 9 | contract TestJoinPolicy is IJoinPolicy, Sponsorship { 10 | 11 | function setParam(uint minimumStake) external pure { 12 | if (minimumStake == 1) { 13 | require(false, "test-error: setting param join policy"); 14 | } else if (minimumStake == 2) { 15 | require(false); 16 | } 17 | } 18 | 19 | // solc-ignore-next-line func-mutability 20 | function onJoin(address, uint amount) external { 21 | if (amount == 100 ether) { 22 | require(false, "test-error: onJoin join policy"); 23 | } else if (amount == 200 ether) { 24 | require(false); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /packages/network-contracts/contracts/OperatorTokenomics/testcontracts/TestKickPolicy.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.13; 4 | 5 | import "../SponsorshipPolicies/IKickPolicy.sol"; 6 | import "../Sponsorship.sol"; 7 | 8 | contract TestKickPolicy is IKickPolicy, Sponsorship { 9 | 10 | function setParam(uint _param) external { 11 | 12 | } 13 | 14 | function onFlag(address operator, address) external { 15 | uint actualSlashingWei = _slash(operator, 10 ether); 16 | _addSponsorship(address(this), actualSlashingWei); 17 | } 18 | 19 | function onVote(address operator, bytes32 voteData, address) external { 20 | uint actualSlashingWei = _kick(operator, uint(voteData)); 21 | _addSponsorship(address(this), actualSlashingWei); 22 | } 23 | 24 | function getFlagData(address operator) override external view returns (uint flagData) { 25 | return operator.balance; 26 | } 27 | 28 | function getMinimumStakeOf(address) override external pure returns (uint individualMinimumStakeWei) { 29 | return 0; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /packages/network-contracts/contracts/OperatorTokenomics/testcontracts/TestToken.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.9; 4 | 5 | import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; 6 | 7 | import "../IERC677.sol"; 8 | import "../IERC677Receiver.sol"; 9 | 10 | /** 11 | * Freely mintable TestToken for contract tests. 12 | * Mostly very similar to DATAv2, TODO: use the real DATAv2 instead. 13 | */ 14 | contract TestToken is ERC20, IERC677 { 15 | constructor (string memory name, string memory symbol) ERC20(name, symbol) { 16 | // solhint-disable-previous-line no-empty-blocks 17 | } 18 | 19 | /** 20 | * @param recipient address where new tokens are transferred (from 0x0) 21 | * @param amount scaled so that 10^18 equals 1 token (multiply by 10^18) 22 | */ 23 | function mint(address recipient, uint amount) external { 24 | _mint(recipient, amount); 25 | } 26 | 27 | function transferAndCall( 28 | address to, 29 | uint256 amount, 30 | bytes calldata data 31 | ) external override returns (bool) { 32 | transfer(to, amount); 33 | 34 | uint256 recipientCodeSize; 35 | assembly { // solhint-disable-line no-inline-assembly 36 | recipientCodeSize := extcodesize(to) 37 | } 38 | if (recipientCodeSize > 0) { 39 | IERC677Receiver receiver = IERC677Receiver(to); 40 | receiver.onTokenTransfer(msg.sender, amount, data); 41 | } 42 | return true; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /packages/network-contracts/contracts/StreamRegistry/ERC2771ContextUpgradeable.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | // We forked this from OpenZeppelin's ERC2771ContextUpgradeable.sol and added the _setTrustedForwarder 4 | // function to be able to set the trusted forwarder address after deployment. 5 | 6 | pragma solidity ^0.8.0; 7 | 8 | import "@openzeppelin/contracts-upgradeable-4.4.2/utils/ContextUpgradeable.sol"; 9 | import "@openzeppelin/contracts-upgradeable-4.4.2/proxy/utils/Initializable.sol"; 10 | 11 | /** 12 | * @dev Context variant with ERC2771 support. 13 | */ 14 | abstract contract ERC2771ContextUpgradeable is Initializable, ContextUpgradeable { 15 | address private _trustedForwarder; 16 | 17 | // solhint-disable-next-line func-name-mixedcase 18 | function __ERC2771Context_init(address trustedForwarder) internal onlyInitializing { 19 | __Context_init_unchained(); 20 | __ERC2771Context_init_unchained(trustedForwarder); 21 | } 22 | 23 | // solhint-disable-next-line func-name-mixedcase 24 | function __ERC2771Context_init_unchained(address trustedForwarder) internal onlyInitializing { 25 | _trustedForwarder = trustedForwarder; 26 | } 27 | 28 | function isTrustedForwarder(address forwarder) public view virtual returns (bool) { 29 | return forwarder == _trustedForwarder; 30 | } 31 | 32 | function _msgSender() internal view virtual override returns (address sender) { 33 | if (isTrustedForwarder(msg.sender)) { 34 | // The assembly code is more direct than the Solidity version using `abi.decode`. 35 | // solhint-disable-next-line no-inline-assembly 36 | assembly { 37 | sender := shr(96, calldataload(sub(calldatasize(), 20))) 38 | } 39 | } else { 40 | return super._msgSender(); 41 | } 42 | } 43 | 44 | function _msgData() internal view virtual override returns (bytes calldata) { 45 | if (isTrustedForwarder(msg.sender)) { 46 | return msg.data[:msg.data.length - 20]; 47 | } else { 48 | return super._msgData(); 49 | } 50 | } 51 | uint256[49] private __gap; 52 | 53 | function _setTrustedForwarder(address trustedForwarder) internal { 54 | _trustedForwarder = trustedForwarder; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /packages/network-contracts/contracts/StreamRegistry/IStreamRegistryV4.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.9; 3 | 4 | /** 5 | * @dev not yet imported by the StreamRegistryV4 contract 6 | * @dev added to fix the compiler version errors for Operator and SponsorshipFactory contracts 7 | */ 8 | interface IStreamRegistryV4 { 9 | enum PermissionType { Edit, Delete, Publish, Subscribe, Grant } 10 | function exists(string calldata streamId) external view returns (bool); 11 | 12 | function createStream(string calldata streamIdPath, string calldata metadataJsonString) external; 13 | function getStreamMetadata(string calldata streamId) external view returns (string memory des); 14 | function updateStreamMetadata(string calldata streamId, string calldata metadata) external; 15 | function grantPublicPermission(string calldata streamId, PermissionType permissionType) external; 16 | function grantPermission(string calldata streamId, address user, PermissionType permissionType) external; 17 | function revokePermission(string calldata streamId, address user, PermissionType permissionType) external; 18 | function addressToString(address _address) external pure returns(string memory); 19 | } 20 | -------------------------------------------------------------------------------- /packages/network-contracts/deployedAddresses.json: -------------------------------------------------------------------------------- 1 | { 2 | "polygon": { 3 | "chainId": 137, 4 | "url": "https://polygon-rpc.com", 5 | "environment": "production", 6 | "contracts": { 7 | "storageNodeRegistry": "0x080F34fec2bc33928999Ea9e39ADc798bEF3E0d6", 8 | "chainlinkOracle": "0x36BF71D0ba2e449fc14f9C4cF51468948E4ED27D", 9 | "enscache": "0x870528c1aDe8f5eB4676AA2d15FC0B034E276A1A", 10 | "streamRegistry": "0x0D483E10612F327FC11965Fc82E90dC19b141641", 11 | "streamStorageRegistry": "0xe8e2660CeDf2a59C917a5ED05B72df4146b58399" 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /packages/network-contracts/generateSelectorsTxt.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | node -p node -p "JSON.stringify(Object.fromEntries(Object.entries(require('.')).filter(([key, value]) => key.endsWith('Codehash'))), null, 4)" > codehashes.json 4 | 5 | cd ../.. 6 | npx hardhat flatten packages/network-contracts/contracts/OperatorTokenomics/testcontracts/MockRandomOracle.sol > temp.sol 7 | grep -v SPDX-License-Identifier temp.sol > temp2.sol 8 | grep -v "pragma experimental" temp2.sol > temp.sol 9 | echo "// SPDX-License-Identifier: MIT" > sol 10 | echo "pragma experimental ABIEncoderV2;" >> sol 11 | cat temp.sol >> sol 12 | docker run --rm -v .:/OperatorTokenomics ethereum/solc:0.8.13 --hashes OperatorTokenomics/sol > packages/network-contracts/selectors.txt 13 | rm temp.sol temp2.sol sol 14 | -------------------------------------------------------------------------------- /packages/network-contracts/logs/2025-05-30-upgrade-peaq-streamregistry.txt: -------------------------------------------------------------------------------- 1 | Checking network peaq 2 | { chainId: 3338, name: 'unknown' } 3 | Checking StreamRegistry at 0xD0C720e99Bd39311614f292d8B0B4e351Bde157c 4 | 0x2de84d9fbdf6d06e2cc584295043dbd76046423b9f8bae9426d4fa5e7c03f4a7 [OK] 5 | Balance of 0xAe1Ba4036610cF18A2Ca6ba0f43DB957ffA21024: 4.852085949003732736 native tokens 6 | Checking new StreamRegistry at 0xD0C720e99Bd39311614f292d8B0B4e351Bde157c 7 | 0xbc6e092677d3938fa44d98435a824f9921ff50822ce3ae9111b53784379cd87e [OK] 8 | Implementation address: 0x4eB94a81a10E7A1C916FC1b6E43986abc6822f7b 9 | Spent 0.020238 ETH for gas 10 | -------------------------------------------------------------------------------- /packages/network-contracts/scripts/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "no-console": "off", 4 | "object-curly-newline": "off", 5 | "max-len": ["warn", { 6 | "code": 150 7 | }] 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /packages/network-contracts/scripts/2023-deployToMumbai.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euxo pipefail 3 | 4 | export CHAIN=mumbai 5 | export IGNORE_TOKEN_SYMBOL=1 6 | npm run deployTokenomicsContracts 7 | 8 | export NEW_ADMIN_ADDRESS=0x63f74A64fd334122aB5D29760C6E72Fb4b752208 9 | export SKIP_REVOKE_CONFIGURATOR=1 10 | export SCRIPT_FILE=scripts/handOverAdminRolesTo.ts 11 | npm run hardhatScript 12 | -------------------------------------------------------------------------------- /packages/network-contracts/scripts/2023-deployToPolygon.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euxo pipefail 3 | 4 | export CHAIN=polygon 5 | npm run deployTokenomicsContracts 6 | 7 | export NEW_ADMIN_ADDRESS=0x63f74A64fd334122aB5D29760C6E72Fb4b752208 8 | export SCRIPT_FILE=scripts/handOverAdminRolesTo.ts 9 | npm run hardhatScript 10 | -------------------------------------------------------------------------------- /packages/network-contracts/scripts/2024-02-20-undelegation-limit-deployment.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euxo pipefail 3 | 4 | npm run build 5 | 6 | # export CHAIN=dev2 7 | # export KEY=0x5e98cce00cff5dea6b454889f359a4ec06b9fa6b88e9d69b86de8e1c81887da0 8 | 9 | export CHAIN=polygon 10 | export GAS_PRICE_GWEI=150 11 | # export KEY=[from 1password] 12 | # export ETHERSCAN_KEY=[from *scan], 13 | 14 | export OUTPUT_FILE=operatorTemplate.txt 15 | ./scripts/upgradeOperatorTemplate.ts 16 | export ADDRESS=$(cat $OUTPUT_FILE) 17 | npm run verify 18 | 19 | export OUTPUT_FILE=undelegationPolicy.txt 20 | ./scripts/upgradeUndelegationPolicy.ts 21 | export ADDRESS=$(cat $OUTPUT_FILE) 22 | npm run verify 23 | 24 | # upgrade can't be done in dev-docker without replacing StreamrConfig deployment with upgrades 25 | # this then would require re-thinking streamrEnvDeployer 26 | # TODO: maybe it wouldn't be so bad to use hardhat in dev-chain-fast deployment? 27 | # exit 0 28 | 29 | export OUTPUT_FILE=streamrConfig.txt 30 | export SCRIPT_FILE=./scripts/upgradeStreamrConfig.ts 31 | npm run hardhatScript 32 | export ADDRESS=$(cat $OUTPUT_FILE) 33 | npm run verify 34 | 35 | # 172800 = 3600 * 48 = 2 days 36 | ./scripts/streamrConfig.ts setMinimumDelegationSeconds 172800 37 | 38 | rm $OUTPUT_FILE 39 | -------------------------------------------------------------------------------- /packages/network-contracts/scripts/2024-02-21-operator-template-redeployment.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euxo pipefail 3 | 4 | npm run build 5 | 6 | # export CHAIN=dev2 7 | # export KEY=0x5e98cce00cff5dea6b454889f359a4ec06b9fa6b88e9d69b86de8e1c81887da0 8 | 9 | export CHAIN=polygon 10 | export GAS_PRICE_GWEI=150 11 | # export KEY=[from 1password] 12 | # export ETHERSCAN_KEY=[from *scan], 13 | 14 | export OUTPUT_FILE=address.txt 15 | ./scripts/upgradeOperatorTemplate.ts 16 | export ADDRESS=$(cat operatorTemplate-address.txt) 17 | npm run verify 18 | export ADDRESS=$(cat nodeModule-address.txt) 19 | npm run verify 20 | export ADDRESS=$(cat queueModule-address.txt) 21 | npm run verify 22 | export ADDRESS=$(cat stakeModule-address.txt) 23 | npm run verify 24 | -------------------------------------------------------------------------------- /packages/network-contracts/scripts/2024-02-22-sip-20.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euxo pipefail 3 | 4 | #npm run build 5 | 6 | # export CHAIN=dev2 7 | # export KEY=0x5e98cce00cff5dea6b454889f359a4ec06b9fa6b88e9d69b86de8e1c81887da0 8 | 9 | export CHAIN=polygon 10 | export GAS_PRICE_GWEI=200 11 | # export KEY=[from 1password] 12 | # export ETHERSCAN_KEY=[from *scan], 13 | 14 | # changing from lower values (5, 50, 0.001, 1.5, 0.5) better change the "incomes" (stake, slashing) first 15 | # if we were changing from higher values, we'd change the "expenses" (rewards) first 16 | ./scripts/streamrConfig.ts setFlagStakeWei 50 17 | ./scripts/streamrConfig.ts setEarlyLeaverPenaltyWei 5000 18 | ./scripts/streamrConfig.ts setSlashingFraction 0.01 19 | ./scripts/streamrConfig.ts setFlaggerRewardWei 36 20 | ./scripts/streamrConfig.ts setFlagReviewerRewardWei 2 21 | -------------------------------------------------------------------------------- /packages/network-contracts/scripts/2024-03-06-operator-template-v2.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euxo pipefail 3 | 4 | npm run build 5 | 6 | # export CHAIN=dev2 7 | # export KEY=0x5e98cce00cff5dea6b454889f359a4ec06b9fa6b88e9d69b86de8e1c81887da0 8 | 9 | export CHAIN=polygon 10 | export GAS_PRICE_GWEI=150 11 | # export KEY=[from 1password] 12 | # export ETHERSCAN_KEY=[from *scan], 13 | 14 | export OUTPUT_FILE=undelegationPolicy.txt 15 | ./scripts/upgradeUndelegationPolicy.ts 16 | export ADDRESS=$(cat undelegationPolicy.txt) 17 | npm run verify 18 | 19 | export OUTPUT_FILE=address.txt 20 | ./scripts/upgradeOperatorTemplate.ts 21 | export ADDRESS=$(cat operatorTemplate-address.txt) 22 | npm run verify 23 | export ADDRESS=$(cat nodeModule-address.txt) 24 | npm run verify 25 | export ADDRESS=$(cat queueModule-address.txt) 26 | npm run verify 27 | export ADDRESS=$(cat stakeModule-address.txt) 28 | npm run verify 29 | -------------------------------------------------------------------------------- /packages/network-contracts/scripts/2024-03-07-temp-low-minimumstake.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euxo pipefail 3 | 4 | #npm run build 5 | 6 | # export CHAIN=dev2 7 | # export KEY=0x5e98cce00cff5dea6b454889f359a4ec06b9fa6b88e9d69b86de8e1c81887da0 8 | 9 | export CHAIN=polygon 10 | export GAS_PRICE_GWEI=150 11 | # export KEY=[from 1password] 12 | # export ETHERSCAN_KEY=[from *scan], 13 | 14 | # the idea of these temporary values is to enable operators to lose their tokens fast with early leaver penalties 15 | # minimum stake becomes 1 DATA, to enable easy staking to the slashing sponsorship 16 | # Calculation: 1% slashing fraction, 0.01 DATA fees => 1% of 1 DATA covers the fees 17 | 18 | ./scripts/streamrConfig.ts setEarlyLeaverPenaltyWei 1000000000 19 | ./scripts/streamrConfig.ts setFlaggerRewardWei 0.003 20 | ./scripts/streamrConfig.ts setFlagReviewerRewardWei 0.001 21 | 22 | # double check 23 | ./scripts/streamrConfig.ts minimumStakeWei 24 | -------------------------------------------------------------------------------- /packages/network-contracts/scripts/2024-03-12-revert-minimumstake.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euxo pipefail 3 | 4 | #npm run build 5 | 6 | # export CHAIN=dev2 7 | # export KEY=0x5e98cce00cff5dea6b454889f359a4ec06b9fa6b88e9d69b86de8e1c81887da0 8 | 9 | export CHAIN=polygon 10 | export GAS_PRICE_GWEI=1000 11 | # export KEY=[from 1password] 12 | # export ETHERSCAN_KEY=[from *scan], 13 | 14 | ./scripts/streamrConfig.ts setEarlyLeaverPenaltyWei 5000 15 | ./scripts/streamrConfig.ts setFlaggerRewardWei 36 16 | ./scripts/streamrConfig.ts setFlagReviewerRewardWei 2 17 | 18 | # double check, should be back to 5000 DATA 19 | ./scripts/streamrConfig.ts minimumStakeWei -------------------------------------------------------------------------------- /packages/network-contracts/scripts/2024-03-15-operator-template-v3.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euxo pipefail 3 | 4 | npm run build 5 | 6 | # export CHAIN=dev2 7 | # export KEY=0x5e98cce00cff5dea6b454889f359a4ec06b9fa6b88e9d69b86de8e1c81887da0 8 | 9 | export CHAIN=polygon 10 | export GAS_PRICE_GWEI=170 11 | # export KEY=[from 1password] 12 | # export ETHERSCAN_KEY=[from *scan], 13 | 14 | export OUTPUT_FILE=undelegationPolicy.txt 15 | ./scripts/upgradeUndelegationPolicy.ts 16 | export ADDRESS=$(cat undelegationPolicy.txt) 17 | npm run verify 18 | 19 | export OUTPUT_FILE=address.txt 20 | ./scripts/upgradeOperatorTemplate.ts 21 | export ADDRESS=$(cat operatorTemplate-address.txt) 22 | npm run verify 23 | export ADDRESS=$(cat nodeModule-address.txt) 24 | npm run verify 25 | export ADDRESS=$(cat queueModule-address.txt) 26 | npm run verify 27 | export ADDRESS=$(cat stakeModule-address.txt) 28 | npm run verify 29 | -------------------------------------------------------------------------------- /packages/network-contracts/scripts/2024-05-03-deployToAmoy.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euxo pipefail 3 | 4 | export CHAIN=polygonAmoy 5 | export SCRIPT_FILE=scripts/deployStreamrContracts.ts 6 | npm run hardhatScript 7 | 8 | export SCRIPT_FILE=scripts/deployTokenomicsContracts.ts 9 | npm run hardhatScript 10 | -------------------------------------------------------------------------------- /packages/network-contracts/scripts/2024-05-22-eth-677-reviewer-selection.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euxo pipefail 3 | 4 | # export CHAIN=dev2 5 | export CHAIN=polygonAmoy 6 | 7 | export SCRIPT_FILE=scripts/upgradeVoteKickPolicy.ts 8 | npm run hardhatScript 9 | -------------------------------------------------------------------------------- /packages/network-contracts/scripts/2024-05-27-streamregistry-v5-amoy.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euxo pipefail 3 | 4 | # export CHAIN=dev2 5 | export CHAIN=polygonAmoy 6 | 7 | export SCRIPT_FILE=scripts/upgradeStreamRegistry.ts 8 | npm run hardhatScript 9 | -------------------------------------------------------------------------------- /packages/network-contracts/scripts/2024-10-22-streamregistry-v5-amoy.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euxo pipefail 3 | 4 | # export CHAIN=dev2 5 | export CHAIN=polygonAmoy 6 | 7 | read -p "Enter deployer private key: " KEY 8 | export KEY="$KEY" 9 | 10 | export CONTRACT_NAME=StreamRegistryV5 11 | export OUTPUT_FILE=newImplementationAddress.txt 12 | export SCRIPT_FILE=scripts/upgradeStreamRegistry.ts 13 | npm run hardhatScript 14 | 15 | read -p "Enter Polygonscan API key: " ETHERSCAN_KEY 16 | export ETHERSCAN_KEY="$ETHERSCAN_KEY" 17 | 18 | export ADDRESS=$(cat newImplementationAddress.txt) 19 | npm run verify 20 | 21 | rm newImplementationAddress.txt 22 | -------------------------------------------------------------------------------- /packages/network-contracts/scripts/2024-10-30-deploy-to-iotex-testnet.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euxo pipefail 3 | 4 | if [ -z "$KEY" ]; then 5 | read -p "Enter deployer private key: " KEY 6 | export KEY="$KEY" 7 | fi 8 | 9 | export CHAIN=iotexTestnet 10 | export SCRIPT_FILE=scripts/deployStreamrContracts.ts 11 | npm run hardhatScript 12 | 13 | read -p "Copy changed addresses to config, then press enter" 14 | 15 | export SCRIPT_FILE=scripts/deployTokenomicsContracts.ts 16 | npm run hardhatScript 17 | -------------------------------------------------------------------------------- /packages/network-contracts/scripts/2024-10-30-deploy-to-iotex.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euxo pipefail 3 | 4 | if declare -p KEY >/dev/null 2>&1; then 5 | echo "Using deployer private key from environment variable KEY" 6 | else 7 | read -p "Enter deployer private key: " KEY 8 | export KEY="$KEY" 9 | fi 10 | 11 | # export CHAIN=iotex 12 | # export CHAIN=iotexTestnet 13 | export CHAIN=dev2 14 | export IGNORE_TOKEN_SYMBOL=1 15 | export OUTPUT_FILE=addresses.json 16 | export SCRIPT_FILE=scripts/deployStreamrContracts.ts 17 | npm run hardhatScript 18 | 19 | export ADDRESS=$(jq -r '.StreamRegistry' addresses.json) 20 | npm run verify 21 | 22 | export ADDRESS=$(jq -r '.ENSCacheV2' addresses.json) 23 | npm run verify 24 | 25 | export ADDRESS=$(jq -r '.StorageNodeRegistry' addresses.json) 26 | npm run verify 27 | 28 | export ADDRESS=$(jq -r '.StreamStorageRegistry' addresses.json) 29 | npm run verify 30 | 31 | cat addresses.json 32 | read -p "Copy above addresses to config, then press enter" 33 | 34 | export SCRIPT_FILE=scripts/deployTokenomicsContracts.ts 35 | npm run hardhatScript 36 | 37 | export ADDRESS=$(jq -r '.StreamrConfig' addresses.json) 38 | npm run verify 39 | 40 | export ADDRESS=$(jq -r '.SponsorshipOperatorContractOnlyJoinPolicy' addresses.json) 41 | npm run verify 42 | 43 | export ADDRESS=$(jq -r '.SponsorshipMaxOperatorsJoinPolicy' addresses.json) 44 | npm run verify 45 | 46 | export ADDRESS=$(jq -r '.SponsorshipStakeWeightedAllocationPolicy' addresses.json) 47 | npm run verify 48 | 49 | export ADDRESS=$(jq -r '.SponsorshipDefaultLeavePolicy' addresses.json) 50 | npm run verify 51 | 52 | export ADDRESS=$(jq -r '.SponsorshipVoteKickPolicy' addresses.json) 53 | npm run verify 54 | 55 | export ADDRESS=$(jq -r '.SponsorshipFactory' addresses.json) 56 | npm run verify 57 | 58 | export ADDRESS=$(jq -r '.OperatorDefaultDelegationPolicy' addresses.json) 59 | npm run verify 60 | 61 | export ADDRESS=$(jq -r '.OperatorDefaultExchangeRatePolicy' addresses.json) 62 | npm run verify 63 | 64 | export ADDRESS=$(jq -r '.OperatorDefaultUndelegationPolicy' addresses.json) 65 | npm run verify 66 | 67 | export ADDRESS=$(jq -r '.OperatorFactory' addresses.json) 68 | npm run verify 69 | -------------------------------------------------------------------------------- /packages/network-contracts/scripts/2024-11-14-streamregistry-v5.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euxo pipefail 3 | 4 | export CHAIN=polygon 5 | # export CHAIN=polygonAmoy 6 | 7 | if declare -p KEY >/dev/null 2>&1; then 8 | echo "Using deployer private key from environment variable KEY" 9 | else 10 | read -p "Enter deployer private key: " KEY 11 | export KEY="$KEY" 12 | fi 13 | 14 | export CONTRACT_NAME=StreamRegistryV5 15 | export OUTPUT_FILE=newImplementationAddress.txt 16 | export SCRIPT_FILE=scripts/upgradeStreamRegistry.ts 17 | npm run hardhatScript 18 | 19 | # Verify & publish the contract source code on Polygonscan 20 | 21 | if declare -p ETHERSCAN_KEY >/dev/null 2>&1; then 22 | echo "Using *scan API key from environment variable ETHERSCAN_KEY" 23 | else 24 | read -p "Enter Polygonscan API key: " ETHERSCAN_KEY 25 | export ETHERSCAN_KEY="$ETHERSCAN_KEY" 26 | fi 27 | 28 | export ADDRESS=$(cat newImplementationAddress.txt) 29 | npm run verify 30 | 31 | rm newImplementationAddress.txt 32 | -------------------------------------------------------------------------------- /packages/network-contracts/scripts/2024-12-04-deploy-to-iotex.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euxo pipefail 3 | 4 | if declare -p KEY >/dev/null 2>&1; then 5 | echo "Using deployer private key from environment variable KEY" 6 | else 7 | read -p "Enter deployer private key: " KEY 8 | export KEY="$KEY" 9 | fi 10 | 11 | export CHAIN=iotex 12 | # export CHAIN=iotexTestnet 13 | # export CHAIN=dev2 14 | # export IGNORE_TOKEN_SYMBOL=1 15 | export OUTPUT_FILE=addresses.json 16 | export SCRIPT_FILE=scripts/deployStreamrContracts.ts 17 | npm run hardhatScript 18 | 19 | export ADDRESS=$(jq -r '.StreamRegistry' addresses.json) 20 | npm run verify 21 | 22 | export ADDRESS=$(jq -r '.ENSCacheV2' addresses.json) 23 | npm run verify 24 | 25 | export ADDRESS=$(jq -r '.StorageNodeRegistry' addresses.json) 26 | npm run verify 27 | 28 | export ADDRESS=$(jq -r '.StreamStorageRegistry' addresses.json) 29 | npm run verify 30 | 31 | cat addresses.json 32 | -------------------------------------------------------------------------------- /packages/network-contracts/scripts/2024-12-13-deploy-to-iotex.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euxo pipefail 3 | 4 | if declare -p KEY >/dev/null 2>&1; then 5 | echo "Using deployer private key from environment variable KEY" 6 | else 7 | read -p "Enter deployer private key: " KEY 8 | export KEY="$KEY" 9 | fi 10 | 11 | export CHAIN=iotex 12 | # export CHAIN=iotexTestnet 13 | # export CHAIN=dev2 14 | # export IGNORE_TOKEN_SYMBOL=1 15 | # export IGNORE_BALANCE=1 16 | export OUTPUT_FILE=addresses.json 17 | export SCRIPT_FILE=scripts/deployTokenomicsContracts.ts 18 | npm run hardhatScript 19 | 20 | export ADDRESS=$(jq -r '.StreamrConfig' addresses.json) 21 | npm run verify 22 | 23 | export ADDRESS=$(jq -r '.SponsorshipOperatorContractOnlyJoinPolicy' addresses.json) 24 | npm run verify 25 | 26 | export ADDRESS=$(jq -r '.SponsorshipMaxOperatorsJoinPolicy' addresses.json) 27 | npm run verify 28 | 29 | export ADDRESS=$(jq -r '.SponsorshipStakeWeightedAllocationPolicy' addresses.json) 30 | npm run verify 31 | 32 | export ADDRESS=$(jq -r '.SponsorshipDefaultLeavePolicy' addresses.json) 33 | npm run verify 34 | 35 | export ADDRESS=$(jq -r '.SponsorshipVoteKickPolicy' addresses.json) 36 | npm run verify 37 | 38 | export ADDRESS=$(jq -r '.SponsorshipFactory' addresses.json) 39 | npm run verify 40 | 41 | export ADDRESS=$(jq -r '.OperatorDefaultDelegationPolicy' addresses.json) 42 | npm run verify 43 | 44 | export ADDRESS=$(jq -r '.OperatorDefaultExchangeRatePolicy' addresses.json) 45 | npm run verify 46 | 47 | export ADDRESS=$(jq -r '.OperatorDefaultUndelegationPolicy' addresses.json) 48 | npm run verify 49 | 50 | export ADDRESS=$(jq -r '.OperatorFactory' addresses.json) 51 | npm run verify 52 | -------------------------------------------------------------------------------- /packages/network-contracts/scripts/2025-05-15-deploy-to-iotex.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euxo pipefail 3 | 4 | if declare -p KEY >/dev/null 2>&1; then 5 | echo "Using deployer private key from environment variable KEY" 6 | else 7 | read -p "Enter deployer private key: " KEY 8 | export KEY="$KEY" 9 | fi 10 | 11 | export CHAIN=iotex 12 | # export CHAIN=iotexTestnet 13 | # export CHAIN=dev2 14 | # export IGNORE_TOKEN_SYMBOL=1 15 | # export IGNORE_BALANCE=1 16 | export OUTPUT_FILE=addresses.json 17 | export SCRIPT_FILE=scripts/deployTokenomicsContracts.ts 18 | npm run hardhatScript 19 | -------------------------------------------------------------------------------- /packages/network-contracts/scripts/calculateFullReviewProbability.ts: -------------------------------------------------------------------------------- 1 | const { 2 | REVIEWER_COUNT = "", 3 | PEER_COUNT = "", 4 | REVIEWER_SELECTION_ITERATIONS = "" 5 | } = process.env 6 | 7 | const reviewerCount = +REVIEWER_COUNT || 5 8 | const peerCount = +PEER_COUNT || 7 9 | const iterations = +REVIEWER_SELECTION_ITERATIONS || 20 10 | 11 | // probability that i reviewers have been picked 12 | let reviewersProbability = Array(reviewerCount + 1).fill(0) 13 | reviewersProbability[0] = 1 14 | 15 | const successProbability = [] 16 | const mostLikely = [] 17 | const mostLikelyProbability = [] 18 | 19 | for (let i = 0; i < iterations; i++) { 20 | const probabilityAfter = Array(reviewerCount + 1).fill(0) 21 | probabilityAfter[reviewerCount] = reviewersProbability[reviewerCount] // if we're done, we won't be undone 22 | for (let j = 0; j < reviewerCount; j++) { 23 | // NOT picking a new reviewer means hitting amount all peers a picked reviewer OR flagger/target (+2) 24 | const pNotPick = (j + 2) / peerCount 25 | probabilityAfter[j] += reviewersProbability[j] * pNotPick // we stay at j 26 | probabilityAfter[j + 1] += reviewersProbability[j] * (1 - pNotPick) // we move on to j + 1 27 | } 28 | reviewersProbability = probabilityAfter 29 | 30 | successProbability[i] = reviewersProbability[reviewerCount] 31 | mostLikelyProbability[i] = Math.max(...reviewersProbability) 32 | mostLikely[i] = reviewersProbability.indexOf(mostLikelyProbability[i]) 33 | } 34 | 35 | console.log("Probability of success after i iterations: ", successProbability.map((p) => +p.toString().slice(0, 6))) 36 | console.log("Most likely number of reviewers after i iterations: ", mostLikely) 37 | console.log("Probability of most likely number of reviewers after i iterations: ", mostLikelyProbability) 38 | 39 | console.log("Success becomes > 50% after", successProbability.findIndex((p) => p > 0.5)) 40 | console.log("Success becomes > 95% after", successProbability.findIndex((p) => p > 0.95)) 41 | console.log("Success becomes > 99% after", successProbability.findIndex((p) => p > 0.99)) 42 | console.log("Success becomes > 1 - 1 / 1 000 000 after", successProbability.findIndex((p) => p > 1 - 1e-6)) 43 | -------------------------------------------------------------------------------- /packages/network-contracts/scripts/data/2024-08-15-0x91/slashings.csv: -------------------------------------------------------------------------------- 1 | OperatorId;OperatorName;Timestamp;BlockNumber;Owner;TotalOperatorTokens;DelegatorsOperatorTokens;Delegator;DelegatorDataLost;Sponsorship 2 | 0xfde4704f1ef76f495761ad6be4b04fb78c2d98c3;HASM;1723333956;60438579;0x13759dfe2d73255e381b031c8dc433c188a58365;5100369000000000000000;5100369000000000000000;0x13759dfe2d73255e381b031c8dc433c188a58365;5000000000000000000000;0x9109abd75eae7e526fc85e33bab24ac45f71717a 3 | 0x5fe3da18aef56417a4e226b54c23cbe4fdf03858;Magellan;1721161437;59442477;0x583eebb8c3178c0ee4fa4c74536b987871ade682;10500617916935460791824;5500617916935460791824;0x583eebb8c3178c0ee4fa4c74536b987871ade682;5000000000000000000000;0x9109abd75eae7e526fc85e33bab24ac45f71717a 4 | 0x4b1bdc4272b5fc63b632ea80dade8317ae90330e;Sagittarius A*;1718815840;58356720;0x056aa97af2fe488e596795b899b26ff4a79bf129;5306266228153026847436;5306266228153026847436;0x056aa97af2fe488e596795b899b26ff4a79bf129;5000000000000000000000;0x9109abd75eae7e526fc85e33bab24ac45f71717a 5 | 0x4b1bdc4272b5fc63b632ea80dade8317ae90330e;Sagittarius A*;1718815646;58356629;0x056aa97af2fe488e596795b899b26ff4a79bf129;10160744467143181216678;10160744467143181216678;0x056aa97af2fe488e596795b899b26ff4a79bf129;5000000000000000000000;0x9109abd75eae7e526fc85e33bab24ac45f71717a 6 | 0x4b1bdc4272b5fc63b632ea80dade8317ae90330e;Sagittarius A*;1718815524;58356572;0x056aa97af2fe488e596795b899b26ff4a79bf129;15015222803909576910581;15015222803909576910581;0x056aa97af2fe488e596795b899b26ff4a79bf129;5000000000000000000000;0x9109abd75eae7e526fc85e33bab24ac45f71717a 7 | 0x4adb0d30f8212112e09a9962aa36ae90b2b03eb1;IATG2001;1715108428;56693687;0x1066a983a43784a5ca8d1d07bbbe001e6b3e4f9f;5438244865703538687527;5438244865703538687527;0x1066a983a43784a5ca8d1d07bbbe001e6b3e4f9f;5000000000000000000000;0x9109abd75eae7e526fc85e33bab24ac45f71717a 8 | -------------------------------------------------------------------------------- /packages/network-contracts/scripts/data/2024-08-15-0x91/slashings.csv.short.csv: -------------------------------------------------------------------------------- 1 | 0x13759dfe2d73255e381b031c8dc433c188a58365,5000000000000000000000 2 | 0x583eebb8c3178c0ee4fa4c74536b987871ade682,5000000000000000000000 3 | 0x056aa97af2fe488e596795b899b26ff4a79bf129,5000000000000000000000 4 | 0x056aa97af2fe488e596795b899b26ff4a79bf129,5000000000000000000000 5 | 0x056aa97af2fe488e596795b899b26ff4a79bf129,5000000000000000000000 6 | 0x1066a983a43784a5ca8d1d07bbbe001e6b3e4f9f,5000000000000000000000 7 | -------------------------------------------------------------------------------- /packages/network-contracts/scripts/data/2024-08-15-0x91/slashings.csv.transfers.csv: -------------------------------------------------------------------------------- 1 | 1723333956,0x9109abd75eae7e526fc85e33bab24ac45f71717a,5000000000000000000000,0xfde4704f1ef76f495761ad6be4b04fb78c2d98c3 2 | 1721161437,0x9109abd75eae7e526fc85e33bab24ac45f71717a,5000000000000000000000,0x5fe3da18aef56417a4e226b54c23cbe4fdf03858 3 | 1718815840,0x9109abd75eae7e526fc85e33bab24ac45f71717a,5000000000000000000000,0x4b1bdc4272b5fc63b632ea80dade8317ae90330e 4 | 1718815646,0x9109abd75eae7e526fc85e33bab24ac45f71717a,5000000000000000000000,0x4b1bdc4272b5fc63b632ea80dade8317ae90330e 5 | 1718815524,0x9109abd75eae7e526fc85e33bab24ac45f71717a,5000000000000000000000,0x4b1bdc4272b5fc63b632ea80dade8317ae90330e 6 | 1715108428,0x9109abd75eae7e526fc85e33bab24ac45f71717a,5000000000000000000000,0x4adb0d30f8212112e09a9962aa36ae90b2b03eb1 7 | -------------------------------------------------------------------------------- /packages/network-contracts/scripts/deployToLivenet/3_deployDelegatedAccessRegistry.ts: -------------------------------------------------------------------------------- 1 | import { JsonRpcProvider } from "@ethersproject/providers" 2 | import { Wallet } from "ethers" 3 | import hhat from "hardhat" 4 | const { ethers } = hhat 5 | 6 | // localsidechain 7 | const chainURL = "http://10.200.10.1:8546" 8 | const privKeyStreamRegistry = "" 9 | 10 | async function main() { 11 | const wallet = new Wallet(privKeyStreamRegistry, new JsonRpcProvider(chainURL)) 12 | 13 | const DelegatedAccessRegistry = await ethers.getContractFactory("DelegatedAccessRegistry", wallet) 14 | 15 | const tx = await DelegatedAccessRegistry.deploy() 16 | 17 | const instance = await tx.deployed() 18 | 19 | console.log(`DelegatedAccessRegistry deployed at ${instance.address}`) 20 | 21 | } 22 | 23 | main() -------------------------------------------------------------------------------- /packages/network-contracts/scripts/deployToLivenet/upgradeStreamRegistry.ts: -------------------------------------------------------------------------------- 1 | import { ethers, upgrades } from "hardhat" 2 | import { networks } from "@streamr/config" 3 | import { parseUnits } from "ethers/lib/utils" 4 | 5 | // Polygon mainet 6 | const STREAMREGISTRYADDRESS = networks.polygon.contracts.StreamRegistry 7 | const DEPLOYMENT_OWNER_KEY = process.env.OCR_ADMIN_PRIVATEKEY || "" 8 | 9 | // localsidechain 10 | // const DEPLOYMENT_OWNER_KEY = '0x4059de411f15511a85ce332e7a428f36492ab4e87c7830099dadbf130f1896ae' 11 | // const STREAMREGISTRYADDRESS = '0x9EffC9A884098180dE81B254e302DFE1598aE3AF' 12 | 13 | async function main() { 14 | 15 | const deploymentOwner = new ethers.Wallet(DEPLOYMENT_OWNER_KEY, ethers.provider) 16 | deploymentOwner.getFeeData = async() => { 17 | console.log("##########################") 18 | return { maxFeePerGas: parseUnits("500", "gwei"), maxPriorityFeePerGas: parseUnits("1", "gwei"), gasPrice: parseUnits("200", "gwei") } 19 | } 20 | const streamregistryFactoryV4 = await ethers.getContractFactory("StreamRegistryV4", deploymentOwner) 21 | console.log("upgrading Streamregistry: proxyaddress: " + STREAMREGISTRYADDRESS) 22 | const streamRegistryUpgraded = await upgrades.upgradeProxy(STREAMREGISTRYADDRESS, streamregistryFactoryV4, {kind: "uups"}) 23 | console.log("streamregistry upgraded, address is (should be same): " + streamRegistryUpgraded.address) 24 | } 25 | 26 | main() 27 | .then(() => process.exit(0)) 28 | .catch((error) => { 29 | console.error(error) 30 | process.exit(1) 31 | }) 32 | 33 | -------------------------------------------------------------------------------- /packages/network-contracts/scripts/deployToLivenet/upgradeStreamStorageRegistry.ts: -------------------------------------------------------------------------------- 1 | import { ethers, upgrades } from "hardhat" 2 | import { networks } from "@streamr/config" 3 | import { parseUnits } from "ethers/lib/utils" 4 | 5 | // Polygon mainet 6 | const STREAMSTORAGEREGISTRYADDRESS = networks.polygon.contracts.StreamStorageRegistry 7 | const DEPLOYMENT_OWNER_KEY = process.env.OCR_ADMIN_PRIVATEKEY || "" 8 | 9 | // localsidechain 10 | // const DEPLOYMENT_OWNER_KEY = '0x4059de411f15511a85ce332e7a428f36492ab4e87c7830099dadbf130f1896ae' 11 | // const STREAMSTORAGEREGISTRYADDRESS = networks.dev1.contracts.StreamStorageRegistry 12 | // const STREAMSTORAGEREGISTRYADDRESS = '0xd57E7d0915c117A1510546b48f6beC551FDa9B93' 13 | 14 | // async function forceImport() { 15 | // const streamStorageRegistryFactory = await ethers.getContractFactory('StreamStorageRegistry') 16 | // await upgrades.forceImport(STREAMSTORAGEREGISTRYADDRESS, 17 | // streamStorageRegistryFactory, {kind: 'uups'}) 18 | // console.log('StreamStorageRegistry imported, check file in .openzeppelin') 19 | // } 20 | 21 | async function main() { 22 | // await forceImport() 23 | 24 | const deploymentOwner = new ethers.Wallet(DEPLOYMENT_OWNER_KEY, ethers.provider) 25 | deploymentOwner.getFeeData = async() => { 26 | console.log("##########################") 27 | return { maxFeePerGas: parseUnits("500", "gwei"), maxPriorityFeePerGas: parseUnits("50", "gwei"), gasPrice: parseUnits("200", "gwei") } 28 | } 29 | const streamstorageregistryFactoryV2 = await ethers.getContractFactory("StreamStorageRegistryV2", deploymentOwner) 30 | console.log("upgrading StreamStorageregistry: proxyaddress: " + STREAMSTORAGEREGISTRYADDRESS) 31 | const streamStorageRegistryUpgraded = await upgrades.upgradeProxy(STREAMSTORAGEREGISTRYADDRESS, streamstorageregistryFactoryV2, {kind: "uups"}) 32 | console.log("streamstorageregistry upgraded, address is (should be same): " + streamStorageRegistryUpgraded.address) 33 | } 34 | 35 | main() 36 | .then(() => process.exit(0)) 37 | .catch((error) => { 38 | console.error(error) 39 | process.exit(1) 40 | }) 41 | 42 | -------------------------------------------------------------------------------- /packages/network-contracts/scripts/prettyPrint.ts: -------------------------------------------------------------------------------- 1 | import type { StreamRegistry } from "@streamr/network-contracts" 2 | import type { ContractReceipt, Event } from "@ethersproject/contracts" 3 | 4 | export type FormattedReceipt = { 5 | blockNumber: number, 6 | from: string, 7 | to: string, 8 | transactionHash: string, 9 | events?: FormattedEvent[], 10 | } 11 | export function formatReceipt(receipt: ContractReceipt): FormattedReceipt { 12 | return { 13 | blockNumber: receipt.blockNumber, 14 | from: receipt.from, 15 | to: receipt.to, 16 | transactionHash: receipt.transactionHash, 17 | events: receipt.events?.map(formatEvent), 18 | } 19 | } 20 | 21 | export type FormattedEvent = { 22 | event: string, 23 | args: Record, 24 | address?: string, 25 | } 26 | export function formatEvent(e: Event): FormattedEvent { 27 | return e.event ? { 28 | event: e.event, 29 | args: !e.args ? {} : Object.fromEntries( 30 | Object.keys(e.args).filter((k) => isNaN(parseInt(k))).map((k) => [k, e.args![k].toString() as string]) 31 | ), 32 | } : { 33 | event: "unknown", 34 | args: {}, 35 | address: e.address, 36 | } 37 | } 38 | 39 | export function formatPermissions(permissions: StreamRegistry.PermissionStructOutput): string[] { 40 | const now = Math.floor(Date.now() / 1000) 41 | return [ 42 | permissions.canGrant ? "grant" : "", 43 | permissions.canEdit ? "edit" : "", 44 | permissions.canDelete ? "delete" : "", 45 | permissions.publishExpiration.gt(now) ? "publish" : "", 46 | permissions.subscribeExpiration.gt(now) ? "subscribe" : "", 47 | ].filter((x) => x) 48 | } 49 | -------------------------------------------------------------------------------- /packages/network-contracts/scripts/removeStorageNode.ts: -------------------------------------------------------------------------------- 1 | // Steps before running this file: 2 | // start dev env: streamr-docker-dev start graph-node 3 | // deploy in the localsidechain: npm run graph (in network-contracts directory!) 4 | 5 | import { Contract } from "@ethersproject/contracts" 6 | import { Wallet } from "@ethersproject/wallet" 7 | import { JsonRpcProvider } from "@ethersproject/providers" 8 | import { getAddress } from "ethers/lib/utils" 9 | 10 | // import type { StreamRegistry, NodeRegistry, StreamStorageRegistry } from '../typechain' 11 | import type { StreamStorageRegistry } from "../typechain" 12 | 13 | // import { address as streamRegistryAddress, abi as streamRegistryAbi } from '../deployments/localsidechain/StreamRegistry.json' 14 | // import { address as nodeRegistryAddress, abi as nodeRegistryAbi } from '../deployments/localsidechain/NodeRegistry.json' 15 | import { address as ssRegistryAddress, abi as ssRegistryAbi } from "../deployments/localsidechain/StreamStorageRegistry.json" 16 | 17 | const SIDECHAINURL = "http://localhost:8546" 18 | const DEFAULTPRIVATEKEY = "0x5e98cce00cff5dea6b454889f359a4ec06b9fa6b88e9d69b86de8e1c81887da0" 19 | 20 | const { 21 | stream = "", 22 | node 23 | } = process.env 24 | 25 | if (!stream) { throw new Error("Missing streamId") } 26 | const streamId = stream || "" 27 | const nodeAddress = getAddress(node || "") 28 | 29 | const provider = new JsonRpcProvider(SIDECHAINURL) 30 | const wallet = new Wallet(DEFAULTPRIVATEKEY, provider) 31 | 32 | async function main() { 33 | // const streamReg = new Contract(streamRegistryAddress, streamRegistryAbi, wallet) as StreamRegistry 34 | // const nodeReg = new Contract(nodeRegistryAddress, nodeRegistryAbi, wallet) as NodeRegistry 35 | const ssReg = new Contract(ssRegistryAddress, ssRegistryAbi, wallet) as StreamStorageRegistry 36 | 37 | const tx3 = await ssReg.removeStorageNode(streamId, nodeAddress) 38 | const tr3 = await tx3.wait() 39 | console.log("Storage node %s removed from stream %s, receipt: %s", nodeAddress, streamId, tr3) 40 | } 41 | 42 | main() 43 | .then(() => process.exit(0)) 44 | .catch((error) => { 45 | console.error(error) 46 | process.exit(1) 47 | }) 48 | 49 | -------------------------------------------------------------------------------- /packages/network-contracts/scripts/tatum/streamrEnvDeployer.ts: -------------------------------------------------------------------------------- 1 | import { Wallet } from "ethers" 2 | import { StreamrEnvDeployer } from "../../src/StreamrEnvDeployer" 3 | 4 | async function main() { 5 | // hardhat node mockchain (first priv key auto-generated by hardhat) 6 | // const key = "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" 7 | const url = "http://127.0.0.1:8547" 8 | 9 | // sidechain key preloaded with ETH (from docker-dev-init) 10 | const key = "0x2cd9855d17e01ce041953829398af7e48b24ece04ff9d0e183414de54dc52285" 11 | // const url = "http://10.200.10.1:8546" 12 | 13 | const streamrEnvDeployer = new StreamrEnvDeployer(key, url) 14 | 15 | await streamrEnvDeployer.deployEnvironment() 16 | await streamrEnvDeployer.createFundStakeSponsorshipAndOperator() 17 | await streamrEnvDeployer.deployEns() 18 | await streamrEnvDeployer.registerEnsName("streamrasd", Wallet.createRandom()) 19 | 20 | const contracts = await streamrEnvDeployer.contracts 21 | await (await contracts.streamRegistry.createStream("/test" + Date.now(), "{}")).wait() 22 | 23 | } 24 | 25 | main() 26 | -------------------------------------------------------------------------------- /packages/network-contracts/scripts/utils/bigint.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env npx ts-node 2 | 3 | import assert from "assert" 4 | 5 | /** Multiply bigint by decimal string, round down */ 6 | export function mul(a: bigint, f: string): bigint { 7 | if (isNaN(parseFloat(f))) { throw new Error("Invalid float: " + f) } 8 | const [int, dec] = f.split(".") 9 | if (!dec) { 10 | return a * BigInt(int) 11 | } 12 | const scale = BigInt(10 ** dec.length) 13 | return (a * (BigInt(int) * scale + BigInt(dec))) / scale 14 | } 15 | 16 | /** Divide bigint by decimal string, round down */ 17 | export function div(a: bigint, f: string): bigint { 18 | if (isNaN(parseFloat(f))) { throw new Error("Invalid float: " + f) } 19 | const [int, dec] = f.split(".") 20 | if (!dec) { 21 | return a * BigInt(int) 22 | } 23 | const scale = BigInt(10 ** dec.length) 24 | return (a * scale * scale / (BigInt(int) * scale + BigInt(dec))) / scale 25 | } 26 | 27 | if (require.main === module) { 28 | // mul tests 29 | assert.equal(mul(BigInt(10), "1"), BigInt(10)) 30 | assert.equal(mul(BigInt(10), "1.5"), BigInt(15)) 31 | assert.equal(mul(BigInt(10), "1.23456"), BigInt(12)) 32 | // assert.equal(mul(87_000_000n, "1.15"), 100_050_000n) // requires ES2020 target 33 | assert.equal(mul( 34 | BigInt("123547812635841762534876235481623548761235487126354"), 35 | "1.00123451823674517826354871623548716253481672354" 36 | ), BigInt("123700334663650685555733683594902667410667690513434")) 37 | 38 | // div tests 39 | assert.equal(div(BigInt(10), "1"), BigInt(10)) 40 | assert.equal(div(BigInt(10), "1.25"), BigInt(8)) 41 | assert.equal(div(BigInt(1000), "1.11111"), BigInt(900)) 42 | // assert.equal(div(100_050_000n, "1.15"), 87_000_000n) 43 | assert.equal(div( 44 | BigInt("123700334663650685555733683594902667410667690513434"), 45 | "1.00123451823674517826354871623548716253481672354" 46 | ), BigInt("123547812635841762534876235481623548761235487126353")) 47 | console.log("[OK]") 48 | } 49 | -------------------------------------------------------------------------------- /packages/network-contracts/scripts/utils/dateToBlockNumber.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env npx ts-node 2 | 3 | // import { Provider } from "@ethersproject/providers" 4 | 5 | /** [date, blockNumber] pairs */ 6 | type DateToBlockNumber = [number, number] 7 | const dateToBlockNumberCache: DateToBlockNumber[] = [] 8 | function findClosestCacheItems(date: number): { index: number, before: DateToBlockNumber, after: DateToBlockNumber } { 9 | let low = 0 10 | let high = dateToBlockNumberCache.length - 1 11 | while (low < high) { 12 | const mid = Math.floor((low + high) / 2) 13 | if (dateToBlockNumberCache[mid][0] < date) { 14 | low = mid + 1 15 | } else { 16 | high = mid 17 | } 18 | } 19 | return { 20 | index: low, 21 | before: dateToBlockNumberCache[low - 1], 22 | after: dateToBlockNumberCache[low], 23 | } 24 | } 25 | 26 | function addToCache(date: number, blockNumber: number) { 27 | const { index, before, after } = findClosestCacheItems(date) 28 | if (before?.[0] === date || after?.[0] === date) { 29 | throw new Error("Date already in cache") 30 | } 31 | dateToBlockNumberCache.splice(index, 0, [date, blockNumber]) 32 | } 33 | 34 | /* 35 | export async function dateToBlockNumber(date: number, provider?: Provider): Promise { 36 | const { before, after } = findClosestCacheItems(date) 37 | if (before[0] === date) { 38 | return before[1] 39 | } 40 | if (after[0] === date) { 41 | return after[1] 42 | } 43 | 44 | // TODO 45 | // const block = await provider.getBlock(mid) 46 | return 0 47 | } 48 | */ 49 | 50 | if (require.main === module) { 51 | const { log } = console 52 | 53 | log("Testing dateToBlockNumber") 54 | 55 | const now = Math.floor(Date.now() / 1000) 56 | log("Empty cache: %o", findClosestCacheItems(now)) 57 | 58 | addToCache(now - 1000, 1000) 59 | log("1 item cache: %o", findClosestCacheItems(now)) 60 | log("1 item cache: %o", findClosestCacheItems(now - 10000)) 61 | 62 | addToCache(now - 100, 2000) 63 | log("2 item cache: %o", findClosestCacheItems(now)) 64 | log("2 item cache: %o", findClosestCacheItems(now - 500)) 65 | log("2 item cache: %o", findClosestCacheItems(now - 1000)) 66 | log("2 item cache: %o", findClosestCacheItems(now - 10000)) 67 | } 68 | -------------------------------------------------------------------------------- /packages/network-contracts/scripts/utils/dateToBlockNumberPolygonSubgraph.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env npx ts-node 2 | 3 | import fetch from "node-fetch" 4 | import { writeFileSync, readFileSync } from "fs" 5 | 6 | import { Logger, TheGraphClient } from "@streamr/utils" 7 | 8 | const dateToBlockNumberCache = new Map() 9 | 10 | function addToCache(date: number, blockNumber: number) { 11 | if (dateToBlockNumberCache.has(date)) { 12 | throw new Error("Date already in cache") 13 | } 14 | dateToBlockNumberCache.set(date, blockNumber) 15 | } 16 | 17 | export function storeCache(): void { 18 | writeFileSync("dateToBlockNumberCache.json", JSON.stringify(dateToBlockNumberCache.entries())) 19 | } 20 | 21 | export function loadCache(): void { 22 | const rawCache = readFileSync("dateToBlockNumberCache.json", "utf-8") 23 | const entries = JSON.parse(rawCache) 24 | entries.forEach(([date, blockNumber]: [number, number]) => { 25 | dateToBlockNumberCache.set(date, blockNumber) 26 | }) 27 | } 28 | 29 | export async function dateToBlockNumber(date: number): Promise { 30 | if (dateToBlockNumberCache.has(date)) { 31 | return dateToBlockNumberCache.get(date)! 32 | } 33 | const client = new TheGraphClient({ 34 | serverUrl: "https://api.thegraph.com/subgraphs/name/matthewlilley/polygon-blocks", 35 | fetch, 36 | logger: new Logger(module) 37 | }) 38 | 39 | const { blocks } = await client.queryEntity({ query: `{ 40 | blocks(where: {timestamp: "${date}"}) { 41 | number 42 | } 43 | }` 44 | }) 45 | if (blocks.length === 0) { 46 | throw new Error(`No block found for date ${date}`) 47 | } 48 | addToCache(date, blocks[0].number) 49 | return parseInt(blocks[0].number) 50 | } 51 | 52 | if (require.main === module) { 53 | const { log } = console 54 | 55 | log("Testing dateToBlockNumber 1709813612") 56 | log("Expecting: 54372204") 57 | dateToBlockNumber(1709813612) 58 | .then((blockNumber) => log("Got: %d", blockNumber)) 59 | .catch(console.error) 60 | } 61 | -------------------------------------------------------------------------------- /packages/network-contracts/scripts/utils/sleep.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env npx ts-node 2 | 3 | export function sleep(ms: number): Promise { 4 | return new Promise((resolve) => { 5 | setTimeout(resolve, ms) 6 | }) 7 | } 8 | 9 | let sleepQueueHead: Promise = Promise.resolve() 10 | /** 11 | * Rate-limited mutex: sleep given ms AFTER the latest queued sleep finishes 12 | * Useful for API call rate-limiting 13 | */ 14 | export function sleepQueue(ms: number): Promise { 15 | sleepQueueHead = sleepQueueHead.then(() => new Promise((resolve) => { 16 | setTimeout(resolve, ms) 17 | })) 18 | return sleepQueueHead 19 | } 20 | 21 | if (require.main === module) { 22 | const { log } = console 23 | 24 | log("Testing sleepQueue 1/sec") 25 | const jobs = ["1", "2", "3", "4", "5"] 26 | void Promise.all(jobs.map(async (x) => { 27 | log("Job %s queued (t=%s)", x, Date.now()) 28 | await sleepQueue(1000) 29 | log("Job %s done (t=%s)", x, Date.now()) 30 | })).then(() => log("Done")) 31 | } 32 | -------------------------------------------------------------------------------- /packages/network-contracts/tasks/copyFilesAfterCompilation.ts: -------------------------------------------------------------------------------- 1 | import { copyFileSync, existsSync } from "fs" 2 | 3 | import { task } from "hardhat/config" 4 | import { TASK_COMPILE } from "hardhat/builtin-tasks/task-names" 5 | 6 | declare module "hardhat/types/config" { 7 | interface HardhatUserConfig { 8 | /** Copy files after compilation has finished successfully 9 | * @param from Source file to copy 10 | * @param to Destination filename 11 | */ 12 | copyFilesAfterCompilation?: { 13 | from: string, 14 | /** Destination directory + filename */ 15 | to: string, 16 | }[]; 17 | } 18 | 19 | interface HardhatConfig { 20 | copyFilesAfterCompilation: [{ 21 | from: string, 22 | to: string, 23 | optional?: boolean, 24 | }] 25 | } 26 | } 27 | 28 | const prefixes = [ 29 | "./", 30 | "./node_modules/", 31 | "../../node_modules/", 32 | "", 33 | ] 34 | 35 | task(TASK_COMPILE, async (_, hre, runSuper) => { 36 | await runSuper() 37 | hre?.config?.copyFilesAfterCompilation?.forEach(({ from, to }) => { 38 | const fromPath = prefixes.map((prefix) => prefix + from).find(existsSync) 39 | if (!fromPath) { 40 | throw new Error(`copyFilesAfterCompilation: File not found: ${from}`) 41 | } 42 | copyFileSync(fromPath, to) 43 | }) 44 | }) 45 | -------------------------------------------------------------------------------- /packages/network-contracts/test/hardhat/OperatorTokenomics/SponsorshipPolicies/MaxOperatorsJoinPolicy.test.ts: -------------------------------------------------------------------------------- 1 | import { ethers as hardhatEthers } from "hardhat" 2 | import { expect } from "chai" 3 | 4 | import { deployTestContracts, TestContracts } from "../deployTestContracts" 5 | import { deploySponsorshipWithoutFactory } from "../deploySponsorshipContract" 6 | 7 | import type { Wallet } from "ethers" 8 | 9 | const { 10 | getSigners, 11 | utils: { parseEther } 12 | } = hardhatEthers 13 | 14 | describe("MaxOperatorsJoinPolicy", (): void => { 15 | let admin: Wallet 16 | let operator: Wallet 17 | let operator2: Wallet 18 | 19 | let contracts: TestContracts 20 | 21 | before(async (): Promise => { 22 | [admin, operator, operator2] = await getSigners() as unknown as Wallet[] 23 | contracts = await deployTestContracts(admin) 24 | }) 25 | 26 | it("will NOT let too many operators join", async function(): Promise { 27 | const { token } = contracts 28 | const sponsorship = await deploySponsorshipWithoutFactory(contracts, { maxOperatorCount: 1 }) 29 | await expect(token.transferAndCall(sponsorship.address, parseEther("5000"), operator.address)) 30 | .to.emit(sponsorship, "OperatorJoined") 31 | await expect(token.transferAndCall(sponsorship.address, parseEther("5000"), operator2.address)) 32 | .to.be.revertedWith("error_tooManyOperators") 33 | }) 34 | }) 35 | -------------------------------------------------------------------------------- /packages/network-contracts/test/hardhat/OperatorTokenomics/deployOperatorContract.ts: -------------------------------------------------------------------------------- 1 | import { Wallet, utils } from "ethers" 2 | import { Operator } from "../../../typechain" 3 | import { TestContracts } from "./deployTestContracts" 4 | 5 | const { parseEther } = utils 6 | 7 | let poolindex = 0 8 | 9 | /** 10 | * @param deployer should be the operator's Wallet 11 | * @param operatorsCutFraction as a fraction of 10^18, like ether 12 | * @returns Promise 13 | */ 14 | export async function deployOperatorContract( 15 | contracts: TestContracts, 16 | deployer: Wallet, 17 | operatorsCutFraction = parseEther("0"), 18 | opts: { 19 | metadata?: string, 20 | overrideDelegationPolicy?: string, 21 | overrideExchangeRatePolicy?: string, 22 | overrideUndelegationPolicy?: string 23 | } = {}, 24 | salt?: string 25 | ): Promise { 26 | const { 27 | operatorFactory, operatorTemplate, 28 | defaultDelegationPolicy, defaultExchangeRatePolicy, defaultUndelegationPolicy 29 | } = contracts 30 | const operatorTokenName = salt ?? `Pool-${Date.now()}-${poolindex++}` 31 | 32 | /** 33 | * @param operatorsCutFraction as a fraction of 10^18, like ether (use parseEther) 34 | * @param stringArgs [0] operatorTokenName, [1] streamMetadata 35 | * @param policies smart contract addresses, must be in the trustedPolicies: [0] delegation, [1] exchange rate, [2] undelegation policy 36 | * @param policyParams not used for default policies: [0] delegation, [1] exchange rate, [2] undelegation policy param 37 | */ 38 | const operatorReceipt = await (await operatorFactory.connect(deployer).deployOperator( 39 | operatorsCutFraction, 40 | operatorTokenName, 41 | opts?.metadata || "{}", 42 | [ 43 | opts?.overrideDelegationPolicy || defaultDelegationPolicy.address, 44 | opts?.overrideExchangeRatePolicy || defaultExchangeRatePolicy.address, 45 | opts?.overrideUndelegationPolicy || defaultUndelegationPolicy.address 46 | ], 47 | [ 0, 0, 0 ] 48 | )).wait() 49 | const newOperatorAddress = operatorReceipt.events?.find((e) => e.event === "NewOperator")?.args?.operatorContractAddress 50 | return operatorTemplate.attach(newOperatorAddress).connect(deployer) 51 | } 52 | -------------------------------------------------------------------------------- /packages/network-contracts/test/hardhat/OperatorTokenomics/utils.ts: -------------------------------------------------------------------------------- 1 | import { ethers as hardhatEthers } from "hardhat" 2 | const { provider: hardhatProvider } = hardhatEthers 3 | import Debug from "debug" 4 | 5 | // export const log = (..._: unknown[]): void => { /* skip logging */ } 6 | export const log = Debug("Streamr::test") 7 | // export const { log } = console // TODO: use pino for logging? 8 | 9 | /** Block timestamp, by default rounded up to nearest million for test log readability */ 10 | export async function getBlockTimestamp(increment = -1): Promise { 11 | const { timestamp } = await hardhatProvider.getBlock("latest") 12 | if (increment > 0) { 13 | return timestamp + increment 14 | } 15 | return Math.floor((timestamp / 1000000) + 1) * 1000000 16 | } 17 | 18 | export async function advanceToTimestamp(timestamp: number, message?: string): Promise { 19 | log("\nt = %s ", timestamp, message ?? "") 20 | await hardhatProvider.send("evm_setNextBlockTimestamp", [timestamp]) 21 | await hardhatProvider.send("evm_mine", [0]) 22 | } 23 | 24 | export const VOTE_KICK = "0x0000000000000000000000000000000000000000000000000000000000000001" 25 | export const VOTE_NO_KICK = "0x0000000000000000000000000000000000000000000000000000000000000000" 26 | export const VOTE_START = 60 * 60 + 10 // 1 hour 27 | export const VOTE_END = VOTE_START + 15 * 60 // +15 minutes 28 | export const END_PROTECTION = VOTE_END + 60 * 60 // +1 hour 29 | -------------------------------------------------------------------------------- /packages/network-contracts/test/hardhat/Registries/getEIP2771MetaTx.ts: -------------------------------------------------------------------------------- 1 | import { ethers } from "hardhat" 2 | import { signTypedData, SignTypedDataVersion, TypedMessage } from "@metamask/eth-sig-util" 3 | import { MinimalForwarder } from "../../../typechain" 4 | 5 | import type { Wallet } from "ethers" 6 | 7 | const { arrayify } = ethers.utils 8 | 9 | interface EIP2771MetaTx { 10 | request: { 11 | from: string 12 | to: string 13 | value: string 14 | gas: string 15 | nonce: string 16 | data: string 17 | } 18 | signature: string 19 | } 20 | 21 | /** @dev see https://eips.ethereum.org/EIPS/eip-2771 */ 22 | export async function getEIP2771MetaTx(to: string, data: string, forwarder: MinimalForwarder, signer: Wallet, gas?: string): Promise { 23 | const request = { 24 | from: signer.address, 25 | to, 26 | value: "0", 27 | gas: gas ? gas : "1000000", 28 | nonce: (await forwarder.getNonce(signer.address)).toString(), 29 | data 30 | } 31 | const d: TypedMessage = { 32 | domain: { 33 | name: "MinimalForwarder", 34 | version: "0.0.1", 35 | chainId: (await ethers.provider.getNetwork()).chainId, 36 | verifyingContract: forwarder.address, 37 | }, 38 | primaryType: "ForwardRequest", 39 | message: request, 40 | types: { 41 | EIP712Domain: [ 42 | { name: "name", type: "string" }, 43 | { name: "version", type: "string" }, 44 | { name: "chainId", type: "uint256" }, 45 | { name: "verifyingContract", type: "address" }, 46 | ], 47 | ForwardRequest: [ 48 | { name: "from", type: "address" }, 49 | { name: "to", type: "address" }, 50 | { name: "value", type: "uint256" }, 51 | { name: "gas", type: "uint256" }, 52 | { name: "nonce", type: "uint256" }, 53 | { name: "data", type: "bytes" }, 54 | ], 55 | }, 56 | } 57 | const options = { 58 | data: d, 59 | privateKey: arrayify(signer.privateKey) as Buffer, 60 | version: SignTypedDataVersion.V4, 61 | } 62 | const signature = signTypedData(options) 63 | return { request, signature } 64 | } 65 | -------------------------------------------------------------------------------- /packages/network-contracts/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "display": "Node 20", 4 | "compilerOptions": { 5 | "lib": ["es2019", "es2020.promise", "es2020.bigint", "es2020.string"], 6 | "target": "es2019", 7 | "module": "commonjs", 8 | "esModuleInterop": true, 9 | "forceConsistentCasingInFileNames": true, 10 | "strict": true, 11 | "skipLibCheck": true, 12 | "resolveJsonModule": true, 13 | "declaration": true, 14 | "outDir": "dist" 15 | }, 16 | "include": ["./src"] 17 | } 18 | -------------------------------------------------------------------------------- /packages/network-contracts/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "display": "Node 12", 4 | "compilerOptions": { 5 | "lib": ["es2019", "es2020.promise", "es2020.bigint", "es2020.string"], 6 | "module": "CommonJS", 7 | "target": "es2019", 8 | "preserveSymlinks": true, 9 | "strict": true, 10 | "esModuleInterop": true, 11 | "skipLibCheck": true, 12 | "forceConsistentCasingInFileNames": true, 13 | "resolveJsonModule": true, 14 | "outDir": "dist" 15 | }, 16 | "include": ["./scripts", "./test", "./typechain", "test-contracts", "./src"], 17 | "files": [ 18 | "./hardhat.config.ts" 19 | ] 20 | } -------------------------------------------------------------------------------- /packages/network-subgraphs/.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules/ 2 | /build 3 | /cache 4 | /bin/ 5 | /generated/ 6 | /abis/ 7 | /tests/.docker/ 8 | /tests/.latest.json -------------------------------------------------------------------------------- /packages/network-subgraphs/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:16-buster 2 | 3 | # RUN apk add libsecret-dev 4 | RUN apt update 5 | RUN apt install -y libsecret-1-dev 6 | RUN mkdir /firstrun && chown node:node /firstrun 7 | RUN mkdir -p /home/node/app && chown -R node:node /home/node/app 8 | 9 | WORKDIR /home/node/app 10 | 11 | USER node 12 | 13 | COPY --chown=node:node . . 14 | 15 | RUN npm i 16 | RUN chmod +x start.sh 17 | ENTRYPOINT [ "sh", "./start.sh" ] 18 | -------------------------------------------------------------------------------- /packages/network-subgraphs/Dockerfile.matchstick: -------------------------------------------------------------------------------- 1 | FROM --platform=linux/x86_64 ubuntu:20.04 2 | 3 | ARG DEBIAN_FRONTEND=noninteractive 4 | 5 | ENV ARGS="" 6 | 7 | RUN apt update \ 8 | && apt install -y sudo curl postgresql \ 9 | && curl -fsSL https://deb.nodesource.com/setup_16.x | sudo bash - \ 10 | && sudo apt install -y nodejs 11 | 12 | RUN curl -OL https://github.com/LimeChain/matchstick/releases/download/0.5.0/binary-linux-20 \ 13 | && chmod a+x binary-linux-20 14 | 15 | RUN mkdir matchstick 16 | WORKDIR /matchstick 17 | 18 | # Commenting out for now as it seems there's no need to copy when using bind mount 19 | # COPY ./ . 20 | 21 | CMD ../binary-linux-20 ${ARGS} -------------------------------------------------------------------------------- /packages/network-subgraphs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@streamr-contracts/network-subgraphs", 3 | "version": "2.4.0", 4 | "description": "Subgraph definitions for the streamr contracts", 5 | "private": true, 6 | "scripts": { 7 | "clean": "rm -rf build generated abis", 8 | "codegen": "graph codegen", 9 | "graphbuild": "graph build", 10 | "build": "./scripts/copyAbisFromContractsPackage.sh && npm run codegen && npm run graphbuild", 11 | "docker:buildLocalArch": "npm run build && docker build . -t streamr/deploy-network-subgraphs:dev-fastchain", 12 | "docker:buildMultiArchAndPush": "npm run build && docker buildx build --platform linux/amd64,linux/arm64 . -t streamr/deploy-network-subgraphs:dev-fastchain --push", 13 | "create-docker-dev": "graph create streamr-dev/network-subgraphs --node http://streamr-dev-thegraph-node-fastchain:8020", 14 | "deploy-docker-dev": "graph deploy streamr-dev/network-subgraphs --version-label v0.0.1 --ipfs http://streamr-dev-ipfs:5001 --node http://streamr-dev-thegraph-node-fastchain:8020", 15 | "create-local": "graph create streamr-dev/network-subgraphs --node http://localhost:8820", 16 | "deploy-local": "graph deploy streamr-dev/network-subgraphs --version-label v0.0.1 --ipfs http://localhost:5001 --node http://localhost:8820", 17 | "redeploy-local": "npm run clean && npm run build && npm run create-local && npm run deploy-local", 18 | "test": "cd ../../ && graph test -d --version 0.5.4", 19 | "coverage": "cd ../../ && graph test -d -- -c" 20 | }, 21 | "devDependencies": { 22 | "@graphprotocol/graph-cli": "0.89.0", 23 | "matchstick-as": "0.5.0" 24 | }, 25 | "dependencies": { 26 | "@graphprotocol/graph-ts": "0.35.1" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /packages/network-subgraphs/scripts/copyAbisFromContractsPackage.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -ex 3 | 4 | pushd ../network-contracts 5 | npm run build 6 | popd 7 | 8 | mkdir -p abis 9 | jq .abi ../network-contracts/artifacts/contracts/NodeRegistry/NodeRegistry.sol/NodeRegistry.json > abis/NodeRegistry.json 10 | jq .abi ../network-contracts/artifacts/contracts/StreamRegistry/StreamRegistryV5.sol/StreamRegistryV5.json > abis/StreamRegistry.json 11 | jq .abi ../network-contracts/artifacts/contracts/StreamStorageRegistry/StreamStorageRegistry.sol/StreamStorageRegistry.json > abis/StreamStorageRegistry.json 12 | 13 | jq .abi ../network-contracts/artifacts/contracts/OperatorTokenomics/StreamrConfig.sol/StreamrConfig.json > abis/StreamrConfig.json 14 | jq .abi ../network-contracts/artifacts/contracts/OperatorTokenomics/Sponsorship.sol/Sponsorship.json > abis/Sponsorship.json 15 | jq .abi ../network-contracts/artifacts/contracts/OperatorTokenomics/SponsorshipFactory.sol/SponsorshipFactory.json > abis/SponsorshipFactory.json 16 | jq .abi ../network-contracts/artifacts/contracts/OperatorTokenomics/Operator.sol/Operator.json > abis/Operator.json 17 | jq .abi ../network-contracts/artifacts/contracts/OperatorTokenomics/OperatorFactory.sol/OperatorFactory.json > abis/OperatorFactory.json 18 | 19 | jq .abi ../hub-contracts/artifacts/contracts/ProjectRegistry/ProjectRegistryV1.sol/ProjectRegistryV1.json > abis/ProjectRegistryV1.json 20 | jq .abi ../hub-contracts/artifacts/contracts/Marketplace/MarketplaceV4.sol/MarketplaceV4.json > abis/MarketplaceV4.json 21 | jq .abi ../hub-contracts/artifacts/contracts/ProjectStaking/ProjectStakingV1.sol/ProjectStakingV1.json > abis/ProjectStakingV1.json 22 | -------------------------------------------------------------------------------- /packages/network-subgraphs/scripts/deployRetry.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # make sure LABEL is set 4 | if [ -z "$LABEL" ]; then 5 | echo "Environment variable LABEL must be set to version to deploy, e.g. v0.0.15" 6 | exit 1 7 | fi 8 | 9 | # make sure SUBGRAPH is set 10 | if [ -z "$SUBGRAPH" ]; then 11 | echo "Environment variable SUBGRAPH must be set to subgraph name, e.g. streamr-amoy-testnet" 12 | exit 1 13 | fi 14 | 15 | retries=0 16 | while true; do 17 | echo "Attempting to deploy streamr-amoy-testnet..." 18 | npx graph deploy $SUBGRAPH -l $LABEL 19 | 20 | if [ $? -eq 0 ]; then 21 | echo "Deployment successful!" 22 | exit 0 23 | else 24 | retries=$((retries + 1)) 25 | echo "Deployment has failed $retries times. Retrying in 10 minutes..." 26 | sleep 600 27 | fi 28 | done 29 | -------------------------------------------------------------------------------- /packages/network-subgraphs/scripts/generateConfigKeys.ts: -------------------------------------------------------------------------------- 1 | #!npx ts-node 2 | 3 | import { ethers } from "ethers" 4 | 5 | const configKeys = [ 6 | "slashingFraction", 7 | "earlyLeaverPenaltyWei", 8 | "minimumSelfDelegationFraction", 9 | "minimumDelegationWei", 10 | "maxPenaltyPeriodSeconds", 11 | "maxQueueSeconds", 12 | "maxAllowedEarningsFraction", 13 | "fishermanRewardFraction", 14 | "protocolFeeFraction", 15 | "protocolFeeBeneficiary", 16 | "minEligibleVoterAge", 17 | "minEligibleVoterFractionOfAllStake", 18 | "flagReviewerCount", 19 | "flagReviewerRewardWei", 20 | "flaggerRewardWei", 21 | "flagReviewerSelectionIterations", 22 | "flagStakeWei", 23 | "reviewPeriodSeconds", 24 | "votingPeriodSeconds", 25 | "flagProtectionSeconds", 26 | "randomOracle", 27 | "trustedForwarder", 28 | "sponsorshipFactory", 29 | "operatorFactory", 30 | "voterRegistry", 31 | "operatorContractOnlyJoinPolicy", 32 | "streamRegistryAddress", 33 | "minimumDelegationSeconds", // added 2024-01-26 (ETH-717) 34 | ] 35 | 36 | let lines = "" 37 | 38 | for (const key of configKeys) { 39 | const value = ethers.utils.keccak256(ethers.utils.toUtf8Bytes(key)) 40 | lines += `const ${key}Key = "${value}"\n` 41 | 42 | } 43 | 44 | // eslint-disable-next-line no-console 45 | console.log(lines) 46 | -------------------------------------------------------------------------------- /packages/network-subgraphs/src/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "prefer-const": "off", 4 | "eqeqeq": "off", 5 | "max-len": ["warn", { 6 | "code": 150 7 | }], 8 | "@typescript-eslint/ban-types": "off", 9 | "no-underscore-dangle": "off" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /packages/network-subgraphs/src/marketplaceV4.ts: -------------------------------------------------------------------------------- 1 | import { log } from '@graphprotocol/graph-ts' 2 | import { ProjectPurchase } from '../generated/schema' 3 | import { ProjectPurchased } from '../generated/MarketplaceV4/MarketplaceV4' 4 | import { loadOrCreateProject } from './helpers' 5 | 6 | export function handleProjectPurchase(event: ProjectPurchased): void { 7 | const projectId = event.params.projectId.toHexString() 8 | const subscriber = event.params.subscriber.toHexString() 9 | const subscriptionSeconds = event.params.subscriptionSeconds.toString() 10 | const price = event.params.price.toString() 11 | const fee = event.params.fee.toString() 12 | log.info('handleProjectPurchase: projectId={} subscriber={} subscriptionSeconds={} price={} fee={} blockNumber={}', 13 | [projectId, subscriber, subscriptionSeconds, price, fee, event.block.number.toString()]) 14 | 15 | let project = loadOrCreateProject(event.params.projectId) 16 | 17 | const newCounter = project.counter + 1 18 | const projectPurchaseId = projectId + '-' + subscriber + '-' + newCounter.toString() 19 | log.info('handleProjectPurchase: projectPurchaseId={}', [projectPurchaseId]) 20 | 21 | const projectPurchase = new ProjectPurchase(projectPurchaseId) 22 | projectPurchase.project = projectId 23 | projectPurchase.subscriber = event.params.subscriber 24 | projectPurchase.subscriptionSeconds = event.params.subscriptionSeconds 25 | projectPurchase.price = event.params.price 26 | projectPurchase.fee = event.params.fee 27 | projectPurchase.purchasedAt = event.block.timestamp 28 | project.counter = newCounter 29 | 30 | let i = project.purchases.indexOf(projectPurchaseId) 31 | if (i < 0) { 32 | let purchasesArray = project.purchases 33 | purchasesArray.push(projectPurchaseId) 34 | project.purchases = purchasesArray 35 | } 36 | 37 | project.save() 38 | projectPurchase.save() 39 | } 40 | -------------------------------------------------------------------------------- /packages/network-subgraphs/src/nodeRegistry.ts: -------------------------------------------------------------------------------- 1 | import { log, store } from '@graphprotocol/graph-ts' 2 | 3 | import { 4 | NodeUpdated, 5 | NodeRemoved, 6 | // NodeWhitelistApproved, 7 | // NodeWhitelistRejected 8 | } from '../generated/NodeRegistry/NodeRegistry' 9 | import { Node } from '../generated/schema' 10 | 11 | export function handleNodeUpdate(event: NodeUpdated): void { 12 | let id = event.params.nodeAddress.toHexString() 13 | let isNew = !event.params.isNew.isZero() 14 | log.info('handleStreamCreation: {} node={} metadata={} blockNumber={}', 15 | [isNew ? 'NEW' : 'UPDATE', id, event.params.metadata, event.block.number.toString()]) 16 | 17 | let node = Node.load(id) 18 | if (!node) { node = new Node(id) } 19 | 20 | node.metadata = event.params.metadata 21 | node.lastSeen = event.params.lastSeen 22 | // node.storedStreams = [] 23 | node.save() 24 | } 25 | 26 | export function handleNodeRemoved(event: NodeRemoved): void { 27 | let id = event.params.nodeAddress.toHexString() 28 | log.info('handleNodeRemoved: node={} blockNumber={}', [id, event.block.number.toString()]) 29 | store.remove('Node', id) 30 | } 31 | 32 | // Problem with whitelisting is that nodes might need to be whitelisted BEFORE they're otherwise created 33 | // So question is: should a "whitelisted node" show up in the graphql results? 34 | // Right now we keep it simple by not indexing the whitelistings at all 35 | // The "rejections" could be interesting to query though so maybe this needs to be solved at some point 36 | 37 | // export function handleNodeWhitelistApproved(event: NodeWhitelistApproved): void { 38 | // const id = event.params.nodeAddress.toHexString() 39 | // log.info('handleNodeWhitelistApproved: node={} blockNumber={}', [id, event.block.number.toString()]) 40 | // let node = Node.load(id) 41 | // node.whitelisted = true 42 | // node.save() 43 | // } 44 | 45 | // export function handleNodeWhitelistRejected(event: NodeWhitelistRejected): void { 46 | // const id = event.params.nodeAddress.toHexString() 47 | // log.info('handleNodeWhitelistRejected: node={} blockNumber={}', [id, event.block.number.toString()]) 48 | // let node = Node.load(id) 49 | // node.whitelisted = false 50 | // node.save() 51 | // } 52 | -------------------------------------------------------------------------------- /packages/network-subgraphs/src/operatorFactory.ts: -------------------------------------------------------------------------------- 1 | import { Address, log } from '@graphprotocol/graph-ts' 2 | import { NewOperator, VoterUpdate } from '../generated/OperatorFactory/OperatorFactory' 3 | import { Operator as OperatorTemplate } from '../generated/templates' 4 | import { loadOrCreateNetwork, loadOrCreateOperator } from './helpers' 5 | 6 | export function handleNewOperator(event: NewOperator): void { 7 | const contractAddress = event.params.operatorContractAddress 8 | log.info('handleNewOperator: operatorAddress={} blockNumber={}', [contractAddress.toHexString(), event.block.number.toString()]) 9 | 10 | // Instantiate template 11 | OperatorTemplate.create(contractAddress) 12 | 13 | const operator = loadOrCreateOperator(contractAddress) 14 | operator.save() 15 | log.info('handleNewOperator: operatorId={}', [operator.id]) 16 | 17 | const network = loadOrCreateNetwork() 18 | network.operatorsCount = network.operatorsCount + 1 19 | network.save() 20 | } 21 | 22 | export function handleVoterUpdate(event: VoterUpdate): void { 23 | const voterRegistryAddress = event.address.toHexString() 24 | const voter = event.params.voterAddress.toHexString() 25 | const isVoter = event.params.isVoter 26 | log.info('handleVoterUpdate: voterRegistryAddress={} voterAddress={} isVoter={} blockNumber={}', 27 | [voterRegistryAddress, voter, isVoter.toString(), event.block.number.toString()]) 28 | 29 | const operator = loadOrCreateOperator(Address.fromString(voter)) 30 | operator.isEligibleToVote = isVoter 31 | operator.save() 32 | 33 | const network = loadOrCreateNetwork() 34 | network.eligibleVotersCount += isVoter ? 1 : -1 35 | network.save() 36 | } 37 | -------------------------------------------------------------------------------- /packages/network-subgraphs/src/streamStorageRegistry.ts: -------------------------------------------------------------------------------- 1 | import { log } from '@graphprotocol/graph-ts' 2 | 3 | import { 4 | Added, 5 | Removed 6 | } from '../generated/StreamStorageRegistry/StreamStorageRegistry' 7 | import { Node } from '../generated/schema' 8 | import { MAX_STREAM_ID_LENGTH } from './helpers' 9 | 10 | export function handleStorageNodeAddedToStream(event: Added): void { 11 | let nodeId = event.params.nodeAddress.toHexString() 12 | let streamId = event.params.streamId.toString() 13 | log.info('handleStorageNodeAddedToStream: stream={} node={} blockNumber={}', [streamId, nodeId, event.block.number.toString()]) 14 | if (event.params.streamId.length > MAX_STREAM_ID_LENGTH) { 15 | log.warning("Overlong stream id not supported: {}", [event.params.streamId]) 16 | return 17 | } 18 | 19 | let node = Node.load(nodeId)! 20 | if (!node.storedStreams) { 21 | node.storedStreams = [streamId] 22 | } else { 23 | let streams = node.storedStreams 24 | if (!streams) { streams = [] } 25 | if (streams.includes(streamId)) { return } 26 | streams.push(streamId) 27 | node.storedStreams = streams 28 | } 29 | node.save() 30 | } 31 | 32 | export function handleStorageNodeRemovedFromStream(event: Removed): void { 33 | let nodeId = event.params.nodeAddress.toHexString() 34 | let streamId = event.params.streamId.toString() 35 | log.info('handleStorageNodeRemovedFromStream: stream={} node={} blockNumber={}', [streamId, nodeId, event.block.number.toString()]) 36 | if (event.params.streamId.length > MAX_STREAM_ID_LENGTH) { 37 | log.warning("Overlong stream id not supported: {}", [event.params.streamId]) 38 | return 39 | } 40 | 41 | let node = Node.load(nodeId)! 42 | if (!node) { return } 43 | if (!node.storedStreams) { return } 44 | let streams = node.storedStreams as string[] 45 | for (let i = 0; i < streams.length; i++) { 46 | let s = streams[i] as string 47 | if (s == streamId) { 48 | streams.splice(i, 1) 49 | node.storedStreams = streams 50 | node.save() 51 | break 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /packages/network-subgraphs/start.sh: -------------------------------------------------------------------------------- 1 | CONTAINER_ALREADY_STARTED="/firstrun/CONTAINER_ALREADY_STARTED_PLACEHOLDER" 2 | GRAPH_NODE_URL="http://streamr-dev-thegraph-node-fastchain:8000" 3 | 4 | wait_for_graph_node_start() { 5 | while true; do 6 | if curl --fail --silent --max-time 1 $GRAPH_NODE_URL; then 7 | echo "Graph Node is ready" 8 | break 9 | else 10 | echo "Waiting for Graph Node to start..." 11 | fi 12 | sleep 1s 13 | done 14 | } 15 | 16 | if [ ! -e $CONTAINER_ALREADY_STARTED ]; then 17 | touch $CONTAINER_ALREADY_STARTED 18 | echo "-- First container startup: wait for Graph Node to start, then deploying subgraph --" 19 | # TODO we could also wait for dev-chain-fast and postgres start, but in practice these are started almost immediately 20 | # and therefore it makes sense to wait only for the Graph Node 21 | wait_for_graph_node_start 22 | npm run create-docker-dev 23 | npm run deploy-docker-dev 24 | else 25 | echo "-- Not first container startup, doing nothing.--" 26 | fi -------------------------------------------------------------------------------- /packages/network-subgraphs/tests/.bin/.gitignore: -------------------------------------------------------------------------------- 1 | *.wasm 2 | -------------------------------------------------------------------------------- /packages/network-subgraphs/tests/integration/event-queue-test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Reproduction of ETH-802 bug 3 | * 1) in network-subgraphs, run `npm run docker:buildLocalArch` 4 | * 2) `streamr-docker-dev start deploy-network-subgraphs-fastchain` 5 | * 3) Open `http://localhost:8800/subgraphs/name/streamr-dev/network-subgraphs/graphql` 6 | * and query `query MyQuery { queueEntries { delegator { id } } }` 7 | * 4) run this file 8 | * 5) Run the query again 9 | * 10 | * Before fix, it would error out with "Cannot return null for non-nullable field QueueEntry.delegator." or similar 11 | * 12 | * TODO: after we have integration tests for real (ETH-639), move this into a proper test 13 | */ 14 | 15 | const { Wallet, Contract, providers } = require("ethers") 16 | const { operatorABI } = require('../../../network-contracts/dist/src/exports') 17 | 18 | const LARGE_NUMBER = "0x01104d6706312fa73d0e00" 19 | 20 | const log = require("debug")("event-queue-test") 21 | 22 | async function main() { 23 | const provider = new providers.JsonRpcProvider("http://10.200.10.1:8547") 24 | 25 | const delegator = new Wallet("0x5e98cce00cff5dea6b454889f359a4ec06b9fa6b88e9d69b86de8e1c81887da0", provider) 26 | const nonDelegator = new Wallet("0xe5af7834455b7239881b85be89d905d6881dcb4751063897f12be1b0dd546bdb", provider) 27 | 28 | const operator = new Contract("0xb63c856cf861a88f4fa8587716fdc4e69cdf9ef1", operatorABI, provider) 29 | 30 | log("Queue before: %o", await operator.undelegationQueue()) 31 | 32 | // clog up the undelegation queue 33 | await operator.connect(delegator).undelegate(LARGE_NUMBER) 34 | 35 | log("Queue after tx 1: %o", await operator.undelegationQueue()) 36 | 37 | // this "non-undelegation" won't be processed 38 | await operator.connect(nonDelegator).undelegate(LARGE_NUMBER) 39 | 40 | log("Queue after tx 2: %o", await operator.undelegationQueue()) 41 | } 42 | main().catch(console.error) -------------------------------------------------------------------------------- /packages/npm-network-contracts/.gitignore: -------------------------------------------------------------------------------- 1 | artifacts 2 | contracts 3 | typechain 4 | abis 5 | -------------------------------------------------------------------------------- /packages/npm-network-contracts/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -ex 3 | 4 | rm -rf contracts artifacts typechain abis 5 | cp -r ../network-contracts/contracts . 6 | 7 | hardhat compile 8 | 9 | # create minified ABIs 10 | artifact_files=$( 11 | find artifacts/contracts -type f -name '*.json' ! -name '*.dbg.json' && 12 | echo "artifacts/@streamr/data-v2/flattened/DATAv2.sol/DATAv2.json" 13 | ) 14 | 15 | for artifact_file in $artifact_files; do 16 | abi_file="$(echo "$artifact_file" | sed 's#^artifacts/#abis/#')" 17 | mkdir -p "$(dirname "$abi_file")" 18 | jq '.abi' "$artifact_file" > "$abi_file" 19 | node minify-abi.mjs "$abi_file" 20 | done 21 | 22 | rm -rf dist 23 | tsc 24 | -------------------------------------------------------------------------------- /packages/npm-network-contracts/hardhat.config.ts: -------------------------------------------------------------------------------- 1 | import { HardhatUserConfig } from "hardhat/config" 2 | import "@nomicfoundation/hardhat-toolbox" 3 | import "hardhat-dependency-compiler" 4 | 5 | const config: HardhatUserConfig = { 6 | solidity: { 7 | compilers: [ 8 | { 9 | version: "0.8.13", 10 | settings: { 11 | optimizer: { 12 | enabled: true, 13 | runs: 100, 14 | }, 15 | }, 16 | }, 17 | { 18 | version: "0.8.9", 19 | settings: { 20 | optimizer: { 21 | enabled: true, 22 | runs: 100, 23 | }, 24 | }, 25 | }, 26 | { // for DATAv2.sol compilation 27 | version: "0.8.6", 28 | settings: { 29 | optimizer: { 30 | enabled: true, 31 | runs: 100, 32 | }, 33 | }, 34 | }, 35 | ], 36 | }, 37 | typechain: { 38 | outDir: "./typechain", 39 | target: "ethers-v6", 40 | }, 41 | dependencyCompiler: { 42 | paths: [ 43 | "@openzeppelin/contracts/metatx/MinimalForwarder.sol", 44 | "@opengsn/contracts/src/forwarder/Forwarder.sol", 45 | "@openzeppelin/contracts-upgradeable/metatx/MinimalForwarderUpgradeable.sol", 46 | "@openzeppelin/contracts/interfaces/IERC1271.sol", 47 | "@ensdomains/ens-contracts/contracts/registry/FIFSRegistrar.sol", 48 | "@ensdomains/ens-contracts/contracts/resolvers/Resolver.sol", 49 | "@ensdomains/ens-contracts/contracts/registry/ENSRegistry.sol", 50 | "@streamr/data-v2/flattened/DATAv2.sol", 51 | ], 52 | }, 53 | } 54 | 55 | export default config 56 | -------------------------------------------------------------------------------- /packages/npm-network-contracts/minify-abi.mjs: -------------------------------------------------------------------------------- 1 | import fs from 'fs' 2 | import { Interface } from 'ethers' 3 | 4 | const filePath = process.argv[2] 5 | const rawAbi = JSON.parse(fs.readFileSync(filePath)) 6 | const iface = new Interface(rawAbi) 7 | const formattedAbi = Object.values(iface.fragments).map(fragment => { 8 | // use full format so that we can use named event parameters 9 | return fragment.format('full') 10 | }) 11 | fs.writeFileSync(filePath, JSON.stringify(formattedAbi, undefined, 4)) 12 | 13 | -------------------------------------------------------------------------------- /packages/npm-network-contracts/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@streamr/network-contracts", 3 | "version": "9.1.0", 4 | "description": "Smart contracts for Streamr Network", 5 | "author": "Streamr Network AG ", 6 | "license": "STREAMR NETWORK OPEN SOURCE LICENSE", 7 | "private": false, 8 | "publishConfig": { 9 | "access": "public" 10 | }, 11 | "files": [ 12 | "dist/src", 13 | "dist/typechain/**/*.d.ts", 14 | "dist/abis/**/*.json" 15 | ], 16 | "main": "./dist/src/exports.js", 17 | "types": "./dist/src/exports.d.ts", 18 | "scripts": { 19 | "build": "./build.sh" 20 | }, 21 | "devDependencies": { 22 | "@chainlink/contracts": "0.3.1", 23 | "@ensdomains/ens-contracts": "1.1.4", 24 | "@nomicfoundation/hardhat-toolbox": "5.0.0", 25 | "@opengsn/provider": "2.2.6", 26 | "@openzeppelin/contracts-upgradeable-4.4.2": "npm:@openzeppelin/contracts-upgradeable@4.4.2", 27 | "@openzeppelin/contracts-upgradeable": "4.8.0", 28 | "@streamr/data-v2": "1.0.4", 29 | "hardhat": "2.22.2", 30 | "hardhat-dependency-compiler": "1.1.4" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /packages/npm-network-contracts/src/exports.ts: -------------------------------------------------------------------------------- 1 | export { default as DATAv2ABI } from '../abis/@streamr/data-v2/flattened/DATAv2.sol/DATAv2.json' 2 | export type { DATAv2 } from '../typechain/@streamr/data-v2/flattened/DATAv2.sol/DATAv2' 3 | 4 | export { default as StreamRegistryABI } from '../abis/contracts/StreamRegistry/StreamRegistryV5.sol/StreamRegistryV5.json' 5 | export type { StreamRegistryV5 as StreamRegistry } from '../typechain/contracts/StreamRegistry/StreamRegistryV5' 6 | 7 | export { default as StreamStorageRegistryABI } from 8 | '../abis/contracts/StreamStorageRegistry/StreamStorageRegistryV2.sol/StreamStorageRegistryV2.json' 9 | export type { StreamStorageRegistryV2 as StreamStorageRegistry } from '../typechain/contracts/StreamStorageRegistry/StreamStorageRegistryV2' 10 | 11 | export { default as NodeRegistryABI } from '../abis/contracts/NodeRegistry/NodeRegistry.sol/NodeRegistry.json' 12 | export type { NodeRegistry } from '../typechain/contracts/NodeRegistry/NodeRegistry' 13 | 14 | export { default as OperatorABI } from '../abis/contracts/OperatorTokenomics/Operator.sol/Operator.json' 15 | export type { Operator } from '../typechain/contracts/OperatorTokenomics/Operator' 16 | 17 | export { default as SponsorshipABI } from '../abis/contracts/OperatorTokenomics/Sponsorship.sol/Sponsorship.json' 18 | export type { Sponsorship } from '../typechain/contracts/OperatorTokenomics/Sponsorship' 19 | 20 | export { default as OperatorFactoryABI } from '../abis/contracts/OperatorTokenomics/OperatorFactory.sol/OperatorFactory.json' 21 | export type { OperatorFactory } from '../typechain/contracts/OperatorTokenomics/OperatorFactory' 22 | 23 | export { default as SponsorshipFactoryABI } from '../abis/contracts/OperatorTokenomics/SponsorshipFactory.sol/SponsorshipFactory.json' 24 | export type { SponsorshipFactory } from '../typechain/contracts/OperatorTokenomics/SponsorshipFactory' 25 | 26 | export { default as StreamrConfigABI } from '../abis/contracts/OperatorTokenomics/StreamrConfigV1_1.sol/StreamrConfigV1_1.json' 27 | export type { StreamrConfigV1_1 as StreamrConfig } from '../typechain/contracts/OperatorTokenomics/StreamrConfigV1_1' 28 | -------------------------------------------------------------------------------- /packages/npm-network-contracts/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "display": "Node 20", 4 | "compilerOptions": { 5 | "lib": ["es2019", "es2020.promise", "es2020.bigint", "es2020.string"], 6 | "target": "es2019", 7 | "module": "commonjs", 8 | "esModuleInterop": true, 9 | "forceConsistentCasingInFileNames": true, 10 | "strict": true, 11 | "skipLibCheck": true, 12 | "resolveJsonModule": true, 13 | "declaration": true, 14 | "outDir": "dist" 15 | }, 16 | "include": ["./src"] 17 | } 18 | -------------------------------------------------------------------------------- /scripts/copyAbis.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -ex 3 | 4 | # start in this /scripts directory 5 | cd "$(dirname "$0")" 6 | 7 | cd ../packages/smartcontracts 8 | npm run build 9 | jq .abi artifacts/contracts/NodeRegistry/NodeRegistry.sol/NodeRegistry.json > ../streamregistry-thegraph-subgraph/abis/NodeRegistry.json 10 | jq .abi artifacts/contracts/StreamRegistry/StreamRegistry.sol/StreamRegistry.json > ../streamregistry-thegraph-subgraph/abis/StreamRegistry.json 11 | jq .abi artifacts/contracts/StreamStorageRegistry/StreamStorageRegistry.sol/StreamStorageRegistry.json > ../streamregistry-thegraph-subgraph/abis/StreamStorageRegistry.json 12 | -------------------------------------------------------------------------------- /scripts/generateYaml.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -ex 3 | 4 | # start in this /scripts directory 5 | cd "$(dirname "$0")" 6 | 7 | cd ../packages/smartcontracts 8 | 9 | export STREAM_REGISTRY_ADDRESS=$(jq .address deployments/localsidechain/StreamRegistry.json) 10 | export NODE_REGISTRY_ADDRESS=$(jq .address deployments/localsidechain/NodeRegistry.json) 11 | export STREAM_STORAGE_REGISTRY_ADDRESS=$(jq .address deployments/localsidechain/StreamStorageRegistry.json) 12 | 13 | cd ../streamregistry-thegraph-subgraph 14 | 15 | cat subgraph.template.yaml |envsubst > subgraph.yaml 16 | --------------------------------------------------------------------------------