├── .nvmrc ├── .npmrc ├── packages ├── ens-sync-script │ ├── .gitignore │ ├── src │ │ └── eth-ens-namehash.d.ts │ ├── README.md │ ├── healthcheck.sh │ ├── Dockerfile │ ├── prepare.sh │ ├── tsconfig.json │ └── package.json ├── network-contracts │ ├── .eslintignore │ ├── .eslintrc.json │ ├── build.sh │ ├── .gitignore │ ├── contracts │ │ ├── NodeRegistry │ │ │ ├── WeightStrategy.sol │ │ │ ├── NodeDomainNameHelper.sol │ │ │ ├── TokenBalanceWeightStrategy.sol │ │ │ ├── WeightedNodeRegistry.sol │ │ │ ├── NetworkParameters.sol │ │ │ └── Ownable.sol │ │ ├── OperatorTokenomics │ │ │ ├── IRandomOracle.sol │ │ │ ├── SponsorshipPolicies │ │ │ │ ├── IJoinPolicy.sol │ │ │ │ ├── ILeavePolicy.sol │ │ │ │ ├── IKickPolicy.sol │ │ │ │ ├── IAllocationPolicy.sol │ │ │ │ ├── OperatorContractOnlyJoinPolicy.sol │ │ │ │ ├── MaxOperatorsJoinPolicy.sol │ │ │ │ ├── DefaultLeavePolicy.sol │ │ │ │ └── AdminKickPolicy.sol │ │ │ ├── OperatorPolicies │ │ │ │ ├── IDelegationPolicy.sol │ │ │ │ ├── INodeModule.sol │ │ │ │ ├── IUndelegationPolicy.sol │ │ │ │ ├── IQueueModule.sol │ │ │ │ ├── IStakeModule.sol │ │ │ │ ├── DefaultDelegationPolicy.sol │ │ │ │ ├── IExchangeRatePolicy.sol │ │ │ │ ├── DefaultUndelegationPolicy.sol │ │ │ │ └── NodeModule.sol │ │ │ ├── IOperator.sol │ │ │ ├── IVoterRegistry.sol │ │ │ └── testcontracts │ │ │ │ ├── MockRandomOracle.sol │ │ │ │ ├── TestJoinPolicy.sol │ │ │ │ ├── TestExchangeRatePolicy2.sol │ │ │ │ ├── TestExchangeRatePolicy.sol │ │ │ │ ├── TestExchangeRatePolicy3.sol │ │ │ │ ├── TestKickPolicy.sol │ │ │ │ └── TestToken.sol │ │ ├── token │ │ │ ├── IERC677Receiver.sol │ │ │ └── IERC677.sol │ │ ├── Hub │ │ │ ├── Marketplace │ │ │ │ ├── IInterchainQueryRouter.sol │ │ │ │ ├── IMailbox.sol │ │ │ │ ├── IInterchainGasPaymaster.sol │ │ │ │ ├── IMessageRecipient.sol │ │ │ │ ├── IPurchaseListener.sol │ │ │ │ ├── IMarketplaceV4.sol │ │ │ │ └── IRemoteMarketplaceV1.sol │ │ │ └── ProjectStaking │ │ │ │ └── IProjectStakingV1.sol │ │ ├── test-contracts │ │ │ ├── ERC20Mintable.sol │ │ │ └── MockMarketplaceBeneficiary.sol │ │ ├── StreamRegistry │ │ │ ├── IStreamRegistryV4.sol │ │ │ └── ERC2771ContextUpgradeable.sol │ │ └── GSN │ │ │ ├── AcceptEverythingPaymaster.sol │ │ │ └── WhitelistPaymaster.sol │ ├── .vscode │ │ ├── settings.json │ │ └── launch.json │ ├── scripts │ │ ├── .eslintrc.json │ │ ├── 2024-05-27-streamregistry-v5-amoy.sh │ │ ├── 2024-05-22-eth-677-reviewer-selection.sh │ │ ├── 2024-05-03-deployToAmoy.sh │ │ ├── 2023-deployToPolygon.sh │ │ ├── 2023-deployToMumbai.sh │ │ ├── data │ │ │ └── 2024-08-15-0x91 │ │ │ │ ├── slashings.csv.short.csv │ │ │ │ ├── slashings.csv.transfers.csv │ │ │ │ └── slashings.csv │ │ ├── 2024-10-30-deploy-to-iotex-testnet.sh │ │ ├── 2025-05-15-deploy-to-iotex.sh │ │ ├── 2024-10-22-streamregistry-v5-amoy.sh │ │ ├── 2024-03-12-revert-minimumstake.sh │ │ ├── hub │ │ │ ├── deployMarketplaceV3.ts │ │ │ ├── upgradeMarketplaceV3.ts │ │ │ ├── deployHubContracts.sh │ │ │ ├── upgradeProjectStaking.ts │ │ │ ├── upgradeProjectRegistry.ts │ │ │ ├── upgradeRemoteMarketplace.ts │ │ │ ├── deployProjectRegistry.ts │ │ │ ├── deployUniswapAdapter.ts │ │ │ ├── deployProjectStakingV1.ts │ │ │ ├── interactStreamRegistry.ts │ │ │ └── deployMarketplaceV4.ts │ │ ├── 2024-02-21-operator-template-redeployment.sh │ │ ├── deployToLivenet │ │ │ ├── 3_deployDelegatedAccessRegistry.ts │ │ │ ├── upgradeStreamRegistry.ts │ │ │ └── upgradeStreamStorageRegistry.ts │ │ ├── 2024-02-22-sip-20.sh │ │ ├── 2024-03-06-operator-template-v2.sh │ │ ├── 2024-03-15-operator-template-v3.sh │ │ ├── 2024-03-07-temp-low-minimumstake.sh │ │ ├── 2024-12-04-deploy-to-iotex.sh │ │ ├── 2024-11-14-streamregistry-v5.sh │ │ ├── utils │ │ │ ├── sleep.ts │ │ │ ├── bigint.ts │ │ │ ├── dateToBlockNumberPolygonSubgraph.ts │ │ │ ├── dateToBlockNumber.ts │ │ │ └── dateToBlockNumberPolygonApi.ts │ │ ├── tatum │ │ │ └── streamrEnvDeployer.ts │ │ ├── 2024-02-20-undelegation-limit-deployment.sh │ │ ├── 2024-12-13-deploy-to-iotex.sh │ │ ├── prettyPrint.ts │ │ ├── 2024-10-30-deploy-to-iotex.sh │ │ ├── calculateFullReviewProbability.ts │ │ ├── removeStorageNode.ts │ │ ├── deployGSN.ts │ │ ├── setTrusted.ts │ │ └── registerStorageNode.ts │ ├── tsconfig.build.json │ ├── deployedAddresses.json │ ├── logs │ │ ├── 2025-05-30-upgrade-peaq-streamregistry.txt │ │ └── 2024-03-07-temp-low-minimumstake.txt │ ├── tsconfig.json │ ├── test │ │ └── hardhat │ │ │ ├── Hub │ │ │ └── constants.ts │ │ │ ├── OperatorTokenomics │ │ │ ├── utils.ts │ │ │ ├── SponsorshipPolicies │ │ │ │ ├── MaxOperatorsJoinPolicy.test.ts │ │ │ │ └── OperatorContractOnlyJoinPolicy.test.ts │ │ │ └── deployOperatorContract.ts │ │ │ └── Registries │ │ │ └── getEIP2771MetaTx.ts │ ├── types │ │ └── hardhat-type-extensions.d.ts │ ├── tasks │ │ └── copyFilesAfterCompilation.ts │ ├── .solhint.json │ ├── doc │ │ └── peaq_deployment.md │ └── generateSelectors.ts ├── network-subgraphs │ ├── tests │ │ ├── .bin │ │ │ └── .gitignore │ │ └── integration │ │ │ └── event-queue-test.js │ ├── .gitignore │ ├── src │ │ ├── .eslintrc.json │ │ ├── operatorFactory.ts │ │ ├── marketplaceV4.ts │ │ ├── streamStorageRegistry.ts │ │ └── nodeRegistry.ts │ ├── Dockerfile │ ├── build.sh │ ├── scripts │ │ ├── fill-handlebars-template.mjs │ │ ├── deployRetry.sh │ │ ├── generateConfigKeys.ts │ │ └── copyAbisFromContractsPackage.sh │ ├── tsconfig.json │ ├── package.json │ └── start.sh ├── config │ ├── .gitignore │ ├── mocharc.json │ ├── docs │ │ └── draft-release-github.png │ ├── release.bash │ ├── scripts │ │ └── generate-types.mjs │ ├── release-npm-update-version-package-json.bash │ ├── release-git-tag.bash │ ├── release-git-validate.bash │ ├── release-validate-semver.bash │ ├── LICENSE │ ├── test │ │ └── index.test.ts │ ├── src │ │ └── index.ts │ ├── Makefile │ └── package.json ├── npm-network-contracts │ ├── .gitignore │ ├── minify-abi.mjs │ ├── tsconfig.json │ ├── build.sh │ ├── package.json │ ├── hardhat.config.ts │ └── src │ │ └── exports.ts └── dev-chain-fast │ ├── start.sh │ ├── Dockerfile │ ├── prepare.sh │ ├── tsconfig.json │ ├── tsconfig.build.json │ ├── scripts │ └── checkTimestamp.ts │ ├── package.json │ └── hardhat.config.js ├── .vscode └── settings.json ├── .eslintignore ├── .gitignore ├── .eslintrc ├── .github ├── workflows │ ├── dependency-review.yaml │ ├── markdown-link-check.yaml │ └── close-stale-issue-and-pr.yaml └── dependabot.yaml ├── scripts └── copyAbis.sh ├── Makefile ├── README.md └── package.json /.nvmrc: -------------------------------------------------------------------------------- 1 | 18.20.5 -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | save-exact=true 2 | save-prefix="" 3 | -------------------------------------------------------------------------------- /packages/ens-sync-script/.gitignore: -------------------------------------------------------------------------------- 1 | heartbeat* 2 | -------------------------------------------------------------------------------- /packages/network-contracts/.eslintignore: -------------------------------------------------------------------------------- 1 | typechain 2 | -------------------------------------------------------------------------------- /packages/network-subgraphs/tests/.bin/.gitignore: -------------------------------------------------------------------------------- 1 | *.wasm 2 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "solidity.defaultCompiler": "remote" 3 | } -------------------------------------------------------------------------------- /packages/config/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | /dist 3 | /*.tgz 4 | src/generated 5 | -------------------------------------------------------------------------------- /packages/npm-network-contracts/.gitignore: -------------------------------------------------------------------------------- 1 | artifacts 2 | contracts 3 | typechain 4 | abis 5 | -------------------------------------------------------------------------------- /packages/dev-chain-fast/start.sh: -------------------------------------------------------------------------------- 1 | npx hardhat node & 2 | node deploy.js & 3 | # keep container running 4 | tail -f /dev/null 5 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | packages/tatum-subgraph/generated/* 2 | packages/network-subgraphs/generated/* 3 | **/typechain 4 | **/build 5 | **/dist -------------------------------------------------------------------------------- /packages/config/mocharc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extension": ["ts"], 3 | "spec": "test/**/*.ts", 4 | "require": "ts-node/register" 5 | } 6 | -------------------------------------------------------------------------------- /packages/config/docs/draft-release-github.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/streamr-dev/network-contracts/HEAD/packages/config/docs/draft-release-github.png -------------------------------------------------------------------------------- /packages/network-contracts/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "semi": ["error", "never"], 4 | "quotes": ["error", "double"] 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /packages/network-subgraphs/.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules/ 2 | /build 3 | /cache 4 | /bin/ 5 | /generated/ 6 | /abis/ 7 | /tests/.docker/ 8 | /tests/.latest.json 9 | subgraph.yaml -------------------------------------------------------------------------------- /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/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 | npx tsc -p tsconfig.build.json 9 | 10 | npx ts-node generateSelectors.ts > selectors.txt 11 | -------------------------------------------------------------------------------- /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/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/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/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/.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/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/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/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-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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /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/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-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-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/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/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 6 | 7 | CMD ["/bin/bash", "/usr/src/hardhat/start.sh"] 8 | # ENTRYPOINT [ "/bin/bash" ] 9 | 10 | 11 | -------------------------------------------------------------------------------- /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/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/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 | cd ../.. 10 | npm run build -w @streamr/network-contracts 11 | cd packages/network-contracts 12 | npm pack 13 | mv streamr-network-contracts-*.tgz ../dev-chain-fast 14 | cd ../dev-chain-fast 15 | npm run build 16 | -------------------------------------------------------------------------------- /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/token/IERC677Receiver.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | interface IERC677Receiver { 5 | /** @dev onTokenTransfer implementation MUST start with check that `msg.sender == tokenAddress` */ 6 | function onTokenTransfer( 7 | address _sender, 8 | uint256 _value, 9 | bytes calldata _data 10 | ) external; 11 | } 12 | -------------------------------------------------------------------------------- /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/Hub/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/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-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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /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/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/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/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/ens-sync-script/prepare.sh: -------------------------------------------------------------------------------- 1 | mkdir -p network-contracts/artifacts/contracts/StreamRegistry/StreamRegistryV5.sol 2 | mkdir -p network-contracts/artifacts/contracts/ENS/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/ENS/ENSCacheV2Streamr.sol/ENSCacheV2Streamr.json ./network-contracts/artifacts/contracts/ENS/ENSCacheV2Streamr.sol 5 | 6 | -------------------------------------------------------------------------------- /packages/network-contracts/contracts/Hub/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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /packages/network-contracts/contracts/Hub/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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /packages/network-subgraphs/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ "$#" -ne 1 ]; then 4 | echo "Usage: $0 " 5 | exit 1 6 | fi 7 | 8 | # TODO could build these to "dist" directory so that we don't need to clean up the subgraph.yaml in package.json (the "rm subgraph.yaml" command) 9 | # - then we can also remove subgraph.yaml from .gitignore 10 | jq '.'$1 subgraph-config.json | node scripts/fill-handlebars-template.mjs subgraph.yaml.hbs > subgraph.yaml 11 | ./scripts/copyAbisFromContractsPackage.sh 12 | npx graph codegen 13 | npx graph build -------------------------------------------------------------------------------- /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/Hub/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/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/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/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/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-subgraphs/scripts/fill-handlebars-template.mjs: -------------------------------------------------------------------------------- 1 | import { readFileSync } from 'fs' 2 | import handlebars from 'handlebars' 3 | 4 | const readFromStdin = async () => { 5 | let result = '' 6 | for await (const chunk of process.stdin) { 7 | result += chunk 8 | } 9 | return result 10 | } 11 | 12 | const main = async () => { 13 | const template = readFileSync(process.argv[2], 'utf8') 14 | const compiled = handlebars.compile(template) 15 | const data = JSON.parse(await readFromStdin()) 16 | process.stdout.write(compiled(data)) 17 | } 18 | 19 | main() 20 | -------------------------------------------------------------------------------- /packages/network-subgraphs/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": ["./tests"] 17 | } -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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/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/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/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-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/hub/deployMarketplaceV3.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable quotes */ 2 | import { ethers, upgrades } from "hardhat" 3 | 4 | const { log } = console 5 | 6 | /** 7 | * npx hardhat run --network dev1 scripts/deployMarketplaceV3.ts 8 | */ 9 | async function main() { 10 | const Marketplace = await ethers.getContractFactory("MarketplaceV3") 11 | const marketplace = await upgrades.deployProxy(Marketplace, [], { kind: 'uups' }) 12 | await marketplace.deployed() 13 | log(`MarketplaceV3 deployed at ${marketplace.address}`) 14 | } 15 | 16 | main().catch((error) => { 17 | console.error(error) 18 | process.exitCode = 1 19 | }) 20 | -------------------------------------------------------------------------------- /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/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/network-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/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/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/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/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/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/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/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", "./types/hardhat-type-extensions.d.ts" 19 | ] 20 | } -------------------------------------------------------------------------------- /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/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/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/contracts/OperatorTokenomics/testcontracts/MockRandomOracle.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.13; 3 | 4 | import "../IRandomOracle.sol"; 5 | 6 | contract MockRandomOracle is IRandomOracle { 7 | bytes32[] public outcomes = [ bytes32(0x1234567812345678123456781234567812345678123456781234567812345678) ]; 8 | uint public index = 0; 9 | 10 | function getRandomBytes32() external returns (bytes32 outcome) { 11 | outcome = outcomes[index]; 12 | index = (index + 1) % outcomes.length; 13 | } 14 | 15 | function setOutcomes(bytes32[] calldata mockRandomBytes32List) external { 16 | require(outcomes.length > 0, "can't set empty outcomes array!"); 17 | outcomes = mockRandomBytes32List; 18 | index = 0; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /packages/network-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 3.0 7 | * See https://forum.openzeppelin.com/t/where-is-erc20mintable-sol-in-openzeppelin-contracts-3-0/2283 8 | * Mostly very similar to DATAv2, TODO: use the real DATAv2 instead. 9 | */ 10 | contract ERC20Mintable is ERC20 { 11 | address private creator; 12 | constructor(string memory name, string memory symbol) ERC20(name, symbol) { 13 | creator = msg.sender; 14 | } 15 | 16 | function mint(address receipient, uint amount) public { 17 | require(msg.sender == creator, "only_creator"); 18 | _mint(receipient, amount); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /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-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-contracts/contracts/Hub/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/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/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-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/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/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-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-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 | "docker:buildLocalArch": "./build.sh dev2 && docker build . -t streamr/deploy-network-subgraphs:dev-fastchain && rm subgraph.yaml", 9 | "docker:buildMultiArchAndPush": "./build.sh dev2 && docker buildx build --platform linux/amd64,linux/arm64 . -t streamr/deploy-network-subgraphs:dev-fastchain --push && rm subgraph.yaml", 10 | "smoke-test": "ts-mocha tests/smoke/smoke.test.ts" 11 | }, 12 | "devDependencies": { 13 | "@graphprotocol/graph-cli": "0.89.0", 14 | "@streamr/config": "^5.8.0", 15 | "handlebars": "4.7.8", 16 | "ts-mocha": "10.0.0" 17 | }, 18 | "dependencies": { 19 | "@graphprotocol/graph-ts": "0.35.1" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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/test/hardhat/Hub/constants.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable quotes */ 2 | 3 | export const types = { 4 | EIP712Domain: [ 5 | { 6 | name: 'name', type: 'string' 7 | }, 8 | { 9 | name: 'version', type: 'string' 10 | }, 11 | { 12 | name: 'chainId', type: 'uint256' 13 | }, 14 | { 15 | name: 'verifyingContract', type: 'address' 16 | }, 17 | ], 18 | ForwardRequest: [ 19 | { 20 | name: 'from', type: 'address' 21 | }, 22 | { 23 | name: 'to', type: 'address' 24 | }, 25 | { 26 | name: 'value', type: 'uint256' 27 | }, 28 | { 29 | name: 'gas', type: 'uint256' 30 | }, 31 | { 32 | name: 'nonce', type: 'uint256' 33 | }, 34 | { 35 | name: 'data', type: 'bytes' 36 | }, 37 | ], 38 | } -------------------------------------------------------------------------------- /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/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/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/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/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/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/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/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 | -------------------------------------------------------------------------------- /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/main/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/main/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/main/packages/network-subgraphs) The Graph subgraphs for many contracts in the `network-contracts` package 8 | - [config](https://github.com/streamr-dev/network-contracts/tree/main/packages/config) Addresses of deployed Streamr contracts on various chains, importable as an npm package -------------------------------------------------------------------------------- /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/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/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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /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/types/hardhat-type-extensions.d.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Hardhat should automatically detect the correct types. For example, the import style we use should work fine: 3 | * import { upgrades, ethers as hardhatEthers } from "hardhat" 4 | * 5 | * However, for some reason, these imports failed without this type definition file. Compilation with "npx tsc" 6 | * resulted in the following errors: 7 | * - Module '"hardhat"' has no exported member 'upgrades' 8 | * - Module '"hardhat"' has no exported member 'ethers' 9 | * 10 | * The root cause might be that we are using outdated versions of Hardhat, OpenZeppelin, or Ethers, or our 11 | * monorepo configuration might be incorrect. When upgrading Hardhat or related packages, we should 12 | * verify whether this file is still necessary. 13 | */ 14 | import type { ethers } from "ethers" 15 | import type { HardhatUpgrades } from "@openzeppelin/hardhat-upgrades" 16 | import "hardhat/types/runtime" 17 | 18 | declare module "hardhat/types/runtime" { 19 | interface HardhatRuntimeEnvironment { 20 | ethers: typeof ethers & HardhatEthersHelpers 21 | upgrades: HardhatUpgrades 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /packages/network-contracts/contracts/test-contracts/MockMarketplaceBeneficiary.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.9; 4 | 5 | import "../Hub/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/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/scripts/hub/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/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/hub/deployHubContracts.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 | if declare -p CHAIN >/dev/null 2>&1; then 12 | echo "Using chain from environment variable CHAIN" 13 | else 14 | read -p "Enter chain: " CHAIN 15 | export CHAIN="$CHAIN" 16 | fi 17 | 18 | OUTPUT_FILE=project-registry-address.txt npx hardhat run --network $CHAIN scripts/hub/deployProjectRegistry.ts 19 | export PROJECT_REGISTRY_ADDRESS=$(cat project-registry-address.txt) 20 | OUTPUT_FILE=project-staking-address.txt npx hardhat run --network $CHAIN scripts/hub/deployProjectStakingV1.ts 21 | OUTPUT_FILE=marketplace-address.txt npx hardhat run --network $CHAIN scripts/hub/deployMarketplaceV4.ts 22 | set +x 23 | echo "{" 24 | echo " \"ProjectRegistryV1\": \"$PROJECT_REGISTRY_ADDRESS\"," 25 | echo " \"MarketplaceV4\": \"$(cat marketplace-address.txt)\"," 26 | echo " \"ProjectStakingV1\": \"$(cat project-staking-address.txt)\"" 27 | echo "}" 28 | 29 | rm project-registry-address.txt 30 | rm project-staking-address.txt 31 | rm marketplace-address.txt 32 | -------------------------------------------------------------------------------- /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 | npx graph create streamr-dev/network-subgraphs --node http://streamr-dev-thegraph-node-fastchain:8020 23 | npx 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 24 | else 25 | echo "-- Not first container startup, doing nothing.--" 26 | fi -------------------------------------------------------------------------------- /packages/network-contracts/scripts/hub/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/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/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/scripts/hub/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/network-contracts/contracts/Hub/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/network-contracts/scripts/hub/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/network-contracts/contracts/Hub/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/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/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/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/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-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/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/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/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.8.3" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /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/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/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/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/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 | "deploy": "tsx ./src/deploy.ts", 17 | "clean": "rm -fr node_modules dist", 18 | "build": "tsc -p tsconfig.build.json" 19 | }, 20 | "dependencies": { 21 | "@ethersproject/constants": "5.7.0", 22 | "@ethersproject/contracts": "5.7.0", 23 | "@ethersproject/hash": "5.7.0", 24 | "@ethersproject/keccak256": "5.7.0", 25 | "@ethersproject/providers": "5.7.0", 26 | "@ethersproject/strings": "5.7.0", 27 | "@ethersproject/units": "5.7.0", 28 | "@ethersproject/wallet": "5.7.0", 29 | "@streamr/network-contracts": "file:../network-contracts", 30 | "hardhat": "2.22.9" 31 | }, 32 | "devDependencies": { 33 | "tsx": "^3.12.7", 34 | "ts-node": "^10.9.1", 35 | "typescript": "^5.8.3" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /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 "../../token/IERC677.sol"; 8 | import "../../token/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 | -------------------------------------------------------------------------------- /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/network-subgraphs", 12 | "./packages/ens-sync-script", 13 | "./packages/dev-chain-fast" 14 | ], 15 | "scripts": { 16 | "build": "npm run build --workspaces --if-present", 17 | "clean": "npm run clean --workspaces --if-present", 18 | "lint": "npm run lint --workspaces --if-present && eslint .", 19 | "test": "npm run test --workspaces --if-present", 20 | "integration-test": "npm run integration-test --workspaces --if-present", 21 | "test:subgraph": "graph test -d", 22 | "e2etest": "npm run e2etest --workspace=network-contracts" 23 | }, 24 | "devDependencies": { 25 | "@chainsafe/ssz": "^0.10.2", 26 | "@types/chai": "4.3.0", 27 | "@types/mocha": "9.1.1", 28 | "@types/node": "16.11.25", 29 | "@typescript-eslint/eslint-plugin": "5.42.1", 30 | "@typescript-eslint/parser": "^5.42.1", 31 | "chai": "4.3.6", 32 | "eslint": "8.27.0", 33 | "eslint-config-streamr-ts": "4.1.0", 34 | "eslint-plugin-chai-friendly": "0.7.2", 35 | "eslint-plugin-promise": "6.1.1", 36 | "gluegun": "5.1.2", 37 | "keyv": "4.5.2", 38 | "mocha": "9.2.0", 39 | "ts-node": "10.4.0", 40 | "typescript": "^5.8.3" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /packages/network-contracts/contracts/Hub/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/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/config/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "5.9.2", 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/main/packages/config#readme" 44 | } 45 | -------------------------------------------------------------------------------- /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-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 ../network-contracts/artifacts/contracts/Hub/ProjectRegistry/ProjectRegistryV1.sol/ProjectRegistryV1.json > abis/ProjectRegistryV1.json 20 | jq .abi ../network-contracts/artifacts/contracts/Hub/Marketplace/MarketplaceV4.sol/MarketplaceV4.json > abis/MarketplaceV4.json 21 | jq .abi ../network-contracts/artifacts/contracts/Hub/ProjectStaking/ProjectStakingV1.sol/ProjectStakingV1.json > abis/ProjectStakingV1.json 22 | -------------------------------------------------------------------------------- /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/hub/deployProjectRegistry.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable quotes */ 2 | import { writeFileSync } from "fs" 3 | 4 | import { ethers as hhEthers, upgrades } from "hardhat" 5 | import { config } from "@streamr/config" 6 | 7 | const { log } = console 8 | 9 | const { 10 | CHAIN = 'dev1', 11 | OUTPUT_FILE, 12 | } = process.env 13 | 14 | const { 15 | contracts: { 16 | StreamRegistry: STREAM_REGISTRY_ADDRESS, // = 0x0000000000000000000000000000000000000000 17 | } 18 | } = (config as any)[CHAIN] 19 | 20 | if (!STREAM_REGISTRY_ADDRESS) { throw new Error(`No StreamRegistry found in chain "${CHAIN}"`) } 21 | // const STREAM_REGISTRY_ADDRESS = "0x0000000000000000000000000000000000000000" 22 | 23 | /** 24 | * npx hardhat run --network dev1 scripts/deployProjectRegistry.ts 25 | * npx hardhat flatten contracts/ProjectRegistry/ProjectRegistryV1.sol > pr.sol 26 | */ 27 | async function main() { 28 | log(`StreamRegistry address: ${STREAM_REGISTRY_ADDRESS}`) 29 | log(`Deploying ProjectRegistryV1 to "${CHAIN}" chain:`) 30 | const projectRegistryFactory = await hhEthers.getContractFactory("ProjectRegistryV1") 31 | const projectRegistryFactoryTx = await upgrades.deployProxy(projectRegistryFactory, [STREAM_REGISTRY_ADDRESS], { kind: 'uups' }) 32 | const projectRegistry = await projectRegistryFactoryTx.deployed() 33 | log(`ProjectRegistryV1 deployed at: ${projectRegistry.address}`) 34 | 35 | if (OUTPUT_FILE) { 36 | writeFileSync(OUTPUT_FILE, projectRegistry.address) 37 | log(`ProjectRegistryV1 address written to ${OUTPUT_FILE}`) 38 | } 39 | } 40 | 41 | main().catch((error) => { 42 | console.error(error) 43 | process.exitCode = 1 44 | }) 45 | -------------------------------------------------------------------------------- /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-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/hub/deployUniswapAdapter.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable quotes */ 2 | import { ethers } from "hardhat" 3 | import { config } from "@streamr/config" 4 | 5 | const { log } = console 6 | 7 | const { 8 | CHAIN = 'dev1', 9 | } = process.env 10 | 11 | const { 12 | contracts: { 13 | MarketplaceV3: MARKETPLACE_ADDRESS, 14 | } 15 | } = (config as any)[CHAIN] 16 | 17 | if (!MARKETPLACE_ADDRESS) { throw new Error(`No MarketplaceV3 found in chain "${CHAIN}"`) } 18 | 19 | /** 20 | * npx hardhat run --network dev scripts/deployUniswapAdapter.ts 21 | */ 22 | async function main(network: string) { 23 | let uniswapV2RouterAddress: string 24 | 25 | switch (network) { 26 | case 'matic': { 27 | uniswapV2RouterAddress = '0xa5E0829CaCEd8fFDD4De3c43696c57F7D7A678ff' 28 | break 29 | } 30 | case 'xdai': { 31 | uniswapV2RouterAddress = '0x1C232F01118CB8B424793ae03F870aa7D0ac7f77' 32 | break 33 | } 34 | case 'dev': { 35 | uniswapV2RouterAddress = '0xeE1bC9a7BFF1fFD913f4c97B6177D47E804E1920' // local docker dev mainchain 36 | break 37 | } 38 | default: { 39 | uniswapV2RouterAddress = '0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D' // Ethereum mainnet, Ropsten, Rinkeby, Görli, Kovan 40 | } 41 | } 42 | 43 | const Uniswap2Adapter = await ethers.getContractFactory("Uniswap2Adapter") 44 | const uniswap2Adapter = await Uniswap2Adapter.deploy(MARKETPLACE_ADDRESS, uniswapV2RouterAddress) 45 | await uniswap2Adapter.deployed() 46 | log(`Uniswap2Adapter deployed at ${uniswap2Adapter.address}`) 47 | } 48 | 49 | main("dev").catch((error) => { 50 | console.error(error) 51 | process.exitCode = 1 52 | }) 53 | -------------------------------------------------------------------------------- /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-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/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/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/network-contracts/scripts/hub/deployProjectStakingV1.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable quotes */ 2 | import { writeFileSync } from "fs" 3 | 4 | import { ethers as hhEthers, upgrades } from "hardhat" 5 | import { config } from "@streamr/config" 6 | 7 | const { log } = console 8 | 9 | const { 10 | CHAIN = 'dev1', 11 | PROJECT_REGISTRY_ADDRESS, 12 | OUTPUT_FILE, 13 | } = process.env 14 | 15 | const { 16 | contracts: { 17 | DATA: STAKING_TOKEN_ADDRESS, // LINK dev1 - 0x3387F44140ea19100232873a5aAf9E46608c791E 18 | ProjectRegistryV1: PROJECT_REGISTRY_ADDRESS_FROM_CONFIG, 19 | } 20 | } = (config as any)[CHAIN] 21 | 22 | const projectRegistryAddress = PROJECT_REGISTRY_ADDRESS || PROJECT_REGISTRY_ADDRESS_FROM_CONFIG 23 | 24 | if (!projectRegistryAddress) { 25 | throw new Error(`No ProjectRegistryV1 found in chain "${CHAIN}", please supply it in env variable PROJECT_REGISTRY_ADDRESS`) 26 | } 27 | 28 | /** 29 | * npx hardhat run --network dev1 scripts/deployProjectStakingV1.ts 30 | * npx hardhat flatten contracts/ProjectStaking/ProjectStakingV1.sol > ps.sol 31 | */ 32 | async function main() { 33 | log(`ProjectRegistryV1 address: ${projectRegistryAddress}`) 34 | log(`Staking token address: ${STAKING_TOKEN_ADDRESS}`) 35 | log(`Deploying ProjectStakingV1 to "${CHAIN}" chain:`) 36 | const projectStakingFactory = await hhEthers.getContractFactory("ProjectStakingV1") 37 | const projectStakingFactoryTx = await upgrades.deployProxy(projectStakingFactory, [ 38 | projectRegistryAddress, 39 | STAKING_TOKEN_ADDRESS 40 | ], { kind: 'uups' }) 41 | const projectStaking = await projectStakingFactoryTx.deployed() 42 | log(`ProjectStakingV1 deployed at: ${projectStaking.address}`) 43 | 44 | if (OUTPUT_FILE) { 45 | writeFileSync(OUTPUT_FILE, projectStaking.address) 46 | log(`ProjectStakingV1 address written to ${OUTPUT_FILE}`) 47 | } 48 | } 49 | 50 | main().catch((error) => { 51 | console.error(error) 52 | process.exitCode = 1 53 | }) 54 | -------------------------------------------------------------------------------- /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/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/.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": 50 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/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/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/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/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-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-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/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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /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-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/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/scripts/hub/interactStreamRegistry.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable quotes */ 2 | import { ethers as hardhatEthers } from "hardhat" 3 | import { config } from "@streamr/config" 4 | import { ProjectRegistryV1, StreamRegistryV5 } from "../../typechain" 5 | 6 | const { getContractFactory } = hardhatEthers 7 | 8 | // export const log = (..._: unknown[]): void => { /* skip logging */ } 9 | const { log } = console 10 | 11 | const { 12 | CHAIN = 'polygon', 13 | } = process.env 14 | 15 | const { 16 | contracts: { 17 | StreamRegistry: STREAM_REGISTRY_ADDRESS, 18 | ProjectRegistry: PROJECT_REGISTRY_ADDRESS, 19 | } 20 | } = (config as any)[CHAIN] 21 | 22 | let projectRegistry: ProjectRegistryV1 23 | let streamRegistry: StreamRegistryV5 24 | 25 | const connectContracts = async () => { 26 | const projectRegistryFactory = await getContractFactory("ProjectRegistryV1") 27 | const projectRegistryFactoryTx = await projectRegistryFactory.attach(PROJECT_REGISTRY_ADDRESS) 28 | projectRegistry = await projectRegistryFactoryTx.deployed() as ProjectRegistryV1 29 | log("ProjectRegistryV1 deployed at: ", projectRegistry.address) 30 | 31 | const streamRegistryFactory = await getContractFactory("StreamRegistryV5") 32 | const streamRegistryFactoryTx = await streamRegistryFactory.attach(STREAM_REGISTRY_ADDRESS) 33 | streamRegistry = await streamRegistryFactoryTx.deployed() as StreamRegistryV5 34 | log("StreamRegistryV5 deployed at: ", streamRegistry.address) 35 | 36 | const latestBlock = await hardhatEthers.provider.getBlock("latest") 37 | log('latestBlock', latestBlock.number) 38 | } 39 | 40 | const grantTrustedRoleToProjectRegistry = async (): Promise => { 41 | log('Granting trusted role...') 42 | const trustedRole = await streamRegistry.TRUSTED_ROLE() 43 | const tx = await streamRegistry.grantRole(trustedRole, projectRegistry.address) 44 | log('tx', tx.hash) 45 | await tx.wait() 46 | log('StreamRegistry granted trusted role to ProjectRegistryV1') 47 | } 48 | 49 | /** 50 | * npx hardhat run --network polygon scripts/interactStreamRegistry.ts 51 | */ 52 | async function main() { 53 | await connectContracts() 54 | 55 | await grantTrustedRoleToProjectRegistry() 56 | } 57 | 58 | main().catch((error) => { 59 | console.error(error) 60 | process.exitCode = 1 61 | }) 62 | -------------------------------------------------------------------------------- /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/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/doc/peaq_deployment.md: -------------------------------------------------------------------------------- 1 | # Peaq deployment notes 2 | 3 | Addresses for the implementation contracts as of 2025-05-28: 4 | ```json 5 | { 6 | "StreamRegistry": "0x3a9A81d576d83FF21f26f325066054540720fC34", 7 | "StreamRegistry proxy": "0xD0C720e99Bd39311614f292d8B0B4e351Bde157c", 8 | "ENSCacheV2": "0xcda36b4a69f4349052d995d2d8b4f3e8a70aa3af", 9 | "ENSCacheV2 proxy": "0x4A71d0FA9e3326E830048B1423d7010fB9343071", 10 | "StreamStorageRegistry": "0x5ABD469031d2B5f939808565EAB8562d7Cbaa939", 11 | "StreamStorageRegistry proxy": "0xB36CF8ee4219a3Ac9FBC3865C35E9a99353c26db", 12 | "StorageNodeRegistry proxy???": "0xaCF9e8134047eDc671162D9404BF63a587435bAa", 13 | "StreamrConfig": "0xFCE1FBFAaE61861B011B379442c8eE1DC868ABd0", 14 | "StreamrConfig proxy": "0xc81Fa3Cc3E81dF24D21bfc608f8FB06262Da4F8c" 15 | } 16 | ``` 17 | the addresses marked "proxy" are the ones found in config.json because the implementations are used via those proxies. 18 | 19 | The `StorageNodeRegistry` is a guess; in deployStreamrContracts.ts, it's deployed as a proxy, but in block explorer only one contract showed up. Not sure what's up with that. 20 | 21 | ## StreamRegistry issue fixed in 2025-05-30 22 | 23 | Before the fix: 24 | ``` 25 | $ CHAIN=peaq STREAM_ID=0xc0147a6a8e21be06edb0703b008f0e732ceea531/peaq/DePIN_1 USER_ID=0xc0147a6a8e21be06edb0703b008f0e732ceea531 SCRIPT_FILE=scripts/check.ts npm run hardhatScript 26 | ... 27 | Checking ENS (cache/bridge) state 28 | Checking the network setup: { chainId: 3338, name: 'unknown' } 29 | StreamRegistry contract at: 0xD0C720e99Bd39311614f292d8B0B4e351Bde157c (true) 30 | ENSCacheV2 contract at: 0x4A71d0FA9e3326E830048B1423d7010fB9343071 (0x0000000000000000000000000000000000000000) 31 | Checking stream '0xc0147a6a8e21be06edb0703b008f0e732ceea531/peaq/DePIN_1' 32 | Metadata: {"partitions":1} 33 | User ID bytes: 0x000000000000000000000000c0147a6a8e21be06edb0703b008f0e732ceea531 34 | 0xc0147a6a8e21be06edb0703b008f0e732ceea531 permissions: [ 'grant', 'edit', 'delete', 'publish', 'subscribe', [length]: 5 ] 35 | 0xc0147a6a8e21be06edb0703b008f0e732ceea531 permissions: [ [length]: 0 ] 36 | ``` 37 | 38 | After the fix: 39 | ``` 40 | Checking stream '0xc0147a6a8e21be06edb0703b008f0e732ceea531/peaq/DePIN_1' 41 | Metadata: {"partitions":1} 42 | 0xc0147a6a8e21be06edb0703b008f0e732ceea531 permissions: [ 'grant', 'edit', 'delete', 'publish', 'subscribe', [length]: 5 ] 43 | 0xc0147a6a8e21be06edb0703b008f0e732ceea531 permissions: [ 'grant', 'edit', 'delete', 'publish', 'subscribe', [length]: 5 ] 44 | ``` 45 | -------------------------------------------------------------------------------- /packages/network-contracts/scripts/utils/dateToBlockNumberPolygonApi.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env npx ts-node 2 | 3 | import fetch from "node-fetch" 4 | import { writeFileSync, readFileSync } from "fs" 5 | 6 | import { sleepQueue } from "./sleep" 7 | 8 | const { 9 | ETHERSCAN_KEY, 10 | CACHE_FILE_NAME = "polygonDateToBlockNumberCache.json", 11 | } = process.env 12 | 13 | const dateToBlockNumberCache = new Map() 14 | 15 | function addToCache(date: number, blockNumber: number) { 16 | if (dateToBlockNumberCache.has(date)) { 17 | throw new Error("Date already in cache") 18 | } 19 | dateToBlockNumberCache.set(date, blockNumber) 20 | storeCache() 21 | } 22 | 23 | export function storeCache(): void { 24 | writeFileSync(CACHE_FILE_NAME, JSON.stringify(Array.from(dateToBlockNumberCache.entries()))) 25 | } 26 | 27 | export function loadCache(): void { 28 | try { 29 | const rawCache = readFileSync(CACHE_FILE_NAME, "utf-8") 30 | const entries: [number, number][] = JSON.parse(rawCache) 31 | entries.forEach(([date, blockNumber]: [number, number]) => { 32 | dateToBlockNumberCache.set(date, blockNumber) 33 | }) 34 | } catch (e) { 35 | console.warn("Failed to load cache", e) 36 | } 37 | } 38 | 39 | export async function dateToBlockNumber(date: number): Promise { 40 | if (dateToBlockNumberCache.has(date)) { 41 | return dateToBlockNumberCache.get(date)! 42 | } 43 | 44 | await sleepQueue(1000) // documented limit: 5 requests per second 45 | const response = await fetch( 46 | "https://api.polygonscan.com/api?module=block&action=getblocknobytime×tamp=" + date.toString() + 47 | "&closest=before&apikey=" + ETHERSCAN_KEY 48 | ) 49 | if (!response.ok) { 50 | throw new Error(`Failed to fetch block number for date ${date}: ${response.status} ${response.statusText}`) 51 | } 52 | 53 | const json = await response.json() 54 | if (json.status !== "1") { 55 | throw new Error(`Failed to fetch block number for date ${date}: ${json.message} / ${json.result}`) 56 | } 57 | 58 | const blockNumber = parseInt(json.result) 59 | if (isNaN(blockNumber)) { 60 | throw new Error(`No block found for date ${date}`) 61 | } 62 | addToCache(date, blockNumber) 63 | return blockNumber 64 | } 65 | 66 | if (require.main === module) { 67 | const { log } = console 68 | 69 | log("Testing dateToBlockNumber 1709766706") 70 | log("Expecting: 54350644") 71 | dateToBlockNumber(1709766706) 72 | .then((blockNumber) => log("Got: %d", blockNumber)) 73 | .catch(console.error) 74 | } 75 | -------------------------------------------------------------------------------- /packages/network-contracts/scripts/deployGSN.ts: -------------------------------------------------------------------------------- 1 | // scripts/deploy.js 2 | // import { Wallet } from "ethers" 3 | // import { ethers, upgrades } from "hardhat" 4 | import { ethers } from "hardhat" 5 | import { WhitelistPaymaster } from "../typechain" 6 | // import { Forwarder } from "../typechain/Forwarder" 7 | 8 | // import { StreamRegistryV4 } from "../typechain/StreamRegistryV4" 9 | 10 | const log = console.log 11 | // const FORWARDER = "0xdA78a11FD57aF7be2eDD804840eA7f4c2A38801d" 12 | 13 | async function main() { 14 | // const forwaderFactory = await ethers.getContractFactory('Forwarder') 15 | // log('Deploying Forwarder...') 16 | // const forwarder = await forwaderFactory.deploy() as Forwarder 17 | // await forwarder.deployed() 18 | // log('Forwarder deployed to:', forwarder.address) 19 | 20 | // // const relayhubFactory = await ethers.getContractFactory('RelayHub') 21 | // // log('Deploying Relayhub...') 22 | // // const relayHub = await relayhubFactory.deploy() as Contract 23 | // // log('Relayhub deployed to:', relayHub.address) 24 | 25 | const paymasterFactory = await ethers.getContractFactory("WhitelistPaymaster") 26 | log("Deploying Paymaster...") 27 | const paymaster = await paymasterFactory.deploy() as WhitelistPaymaster 28 | await paymaster.deployed() 29 | log("Paymaster deployed to:", paymaster.address) 30 | 31 | // const streamRegistryFactory = await ethers.getContractFactory('StreamRegistryV4') 32 | // // const streamRegistryFactoryTx = await streamRegistryFactory.deploy(ensCache.address, constants.AddressZero) 33 | // const streamRegistryFactoryTx = await upgrades.deployProxy(streamRegistryFactory, 34 | // [Wallet.createRandom().address, forwarder.address], { kind: 'uups' }) 35 | // const streamRegistry = await streamRegistryFactoryTx.deployed() as StreamRegistryV4 36 | // log(`Streamregistry deployed at ${streamRegistry.address}`) 37 | // log('setting relay hub in paymaster') 38 | // await (await paymaster.setRelayHub(streamRegistry.signer.getAddress())).wait() 39 | // log('sending 1 eth to paymaster') 40 | // const [owner] = await ethers.getSigners(); 41 | // // const balance = (await ethers.getDefaultProvider().getBalance(owner.address)).toString() 42 | // // const tx = await owner.sendTransaction({ to: paymaster.address, 43 | // // value: 10 }) 44 | // // await tx.wait() 45 | 46 | // log('setting target in paymaster') 47 | // await (await paymaster.setTrustedForwarder(forwarder.address)).wait() 48 | } 49 | 50 | main() 51 | .then(() => process.exit(0)) 52 | .catch((error) => { 53 | console.error(error) 54 | process.exit(1) 55 | }) 56 | -------------------------------------------------------------------------------- /packages/network-contracts/test/hardhat/OperatorTokenomics/SponsorshipPolicies/OperatorContractOnlyJoinPolicy.test.ts: -------------------------------------------------------------------------------- 1 | import { ethers as hardhatEthers } from "hardhat" 2 | import { expect } from "chai" 3 | 4 | import { deployTestContracts, TestContracts } from "../deployTestContracts" 5 | import { deployOperatorContract } from "../deployOperatorContract" 6 | import { deploySponsorship } from "../deploySponsorshipContract" 7 | 8 | import type { Wallet } from "ethers" 9 | import type { Operator } from "../../../../typechain" 10 | 11 | const { 12 | getSigners, 13 | getContractFactory, 14 | utils: { parseEther } 15 | } = hardhatEthers 16 | 17 | describe("OperatorContractOnlyJoinPolicy", (): void => { 18 | let admin: Wallet 19 | let operator: Wallet 20 | 21 | let contracts: TestContracts 22 | 23 | before(async (): Promise => { 24 | [admin, operator] = await getSigners() as unknown as Wallet[] 25 | contracts = await deployTestContracts(admin) 26 | 27 | const { token } = contracts 28 | await (await token.mint(operator.address, parseEther("10000"))).wait() 29 | }) 30 | 31 | it("only allows Operators to stake", async function(): Promise { 32 | const { streamrConfig, token } = contracts 33 | const sponsorship = await deploySponsorship(contracts) 34 | 35 | await (await token.approve(sponsorship.address, parseEther("5000"))).wait() 36 | await expect(sponsorship.stake(operator.address, parseEther("5000"))) 37 | .to.be.revertedWith("error_onlyOperators") 38 | 39 | const badOp = await (await (await getContractFactory("Operator", operator)).deploy()).deployed() as Operator 40 | await (await badOp.initialize(token.address, streamrConfig.address, operator.address, "testpool", "{}", "1", 41 | [contracts.nodeModule.address, contracts.queueModule.address, contracts.stakeModule.address])).wait() 42 | await (await badOp.setExchangeRatePolicy(contracts.defaultExchangeRatePolicy.address, "0")).wait() 43 | await (await token.connect(operator).transferAndCall(badOp.address, parseEther("5000"), "0x")).wait() 44 | await expect(badOp.stake(sponsorship.address, parseEther("5000"))) 45 | .to.be.revertedWith("error_onlyOperators") 46 | 47 | const goodOp = await deployOperatorContract(contracts, operator) 48 | await (await token.connect(operator).transferAndCall(goodOp.address, parseEther("5000"), "0x")).wait() 49 | await expect(goodOp.stake(sponsorship.address, parseEther("5000"))) 50 | .to.emit(sponsorship, "OperatorJoined").withArgs(goodOp.address) 51 | .to.emit(goodOp, "Staked") 52 | }) 53 | }) 54 | -------------------------------------------------------------------------------- /packages/network-contracts/generateSelectors.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable quotes, no-console */ 2 | 3 | import { ethers } from 'ethers' 4 | import * as fs from 'fs' 5 | import * as path from 'path' 6 | 7 | interface AbiItem { 8 | name: string 9 | inputs: { 10 | type: string 11 | }[] 12 | type: string 13 | } 14 | 15 | interface ContractArtifact { 16 | abi: AbiItem[] 17 | } 18 | 19 | const SECTION_TYPES = ['function', 'error', 'event'] 20 | const ABI_FILE_NAME_SUFFIX = '.json' 21 | const DEBUG_FILE_NAME_SUFFIX = '.dbg.json' 22 | 23 | function crawlDirectory(directoryPath: string): string[] { 24 | const results: string[] = [] 25 | const entries = fs.readdirSync(directoryPath, { withFileTypes: true }) 26 | for (const entry of entries) { 27 | const entryPath = path.join(directoryPath, entry.name) 28 | if (entry.isDirectory()) { 29 | results.push(...crawlDirectory(entryPath)) 30 | } else if (entry.isFile()) { 31 | results.push(entryPath) 32 | } 33 | } 34 | return results 35 | } 36 | 37 | function generateSelectorForItem(item: AbiItem, isFunction: boolean): string { 38 | const signature = `${item.name}(${item.inputs.map((input) => input.type).join(',')})` 39 | const selector = ethers.utils.id(signature).slice(0, 10) // first 4 bytes and the 0x prefix 40 | const suffix = isFunction ? '()' : '' 41 | return `${selector} ${item.name}${suffix}` 42 | } 43 | 44 | function generateSelectorsForArtifacts(filePath: string): string { 45 | try { 46 | const fileName = filePath.split('/').pop()! 47 | const contractName = fileName.slice(0, fileName.length - ABI_FILE_NAME_SUFFIX.length) 48 | const artifact: ContractArtifact = JSON.parse(fs.readFileSync(filePath, 'utf-8')) 49 | return SECTION_TYPES 50 | .filter((sectionType) => artifact.abi.some((item) => item.type === sectionType)) 51 | .map((sectionType) => { 52 | const header = `# ${contractName} ${sectionType}s` 53 | const items = artifact.abi.filter((item) => item.type === sectionType) 54 | return [header, ...items.map((item) => generateSelectorForItem(item, (sectionType === 'function')))].join('\n') 55 | }) 56 | .join('\n\n') 57 | } catch (error) { 58 | console.error(`Error processing ${filePath}`) 59 | console.error(error) 60 | process.exit(1) 61 | } 62 | } 63 | 64 | console.log(crawlDirectory('artifacts/contracts') 65 | .filter((filePath) => !filePath.endsWith(DEBUG_FILE_NAME_SUFFIX)) 66 | .filter((filePath) => !filePath.includes('testcontracts')) 67 | .map((contract) => generateSelectorsForArtifacts(contract)) 68 | .join('\n\n\n\n')) -------------------------------------------------------------------------------- /packages/network-contracts/scripts/hub/deployMarketplaceV4.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable quotes */ 2 | import { writeFileSync } from "fs" 3 | import { ethers as hardhatEthers, upgrades } from "hardhat" 4 | import { config } from "@streamr/config" 5 | import { utils } from "ethers" 6 | import { chainToMailboxAddress } from "./utils" 7 | 8 | const { getContractFactory } = hardhatEthers 9 | const { id } = utils 10 | const { log } = console 11 | 12 | const { 13 | CHAIN = 'dev1', 14 | PROJECT_REGISTRY_ADDRESS, 15 | OUTPUT_FILE, 16 | } = process.env 17 | 18 | const { 19 | id: CHAIN_ID, 20 | contracts: { 21 | ProjectRegistryV1: PROJECT_REGISTRY_ADDRESS_FROM_CONFIG, 22 | } 23 | } = (config as any)[CHAIN] 24 | 25 | const projectRegistryAddress = PROJECT_REGISTRY_ADDRESS || PROJECT_REGISTRY_ADDRESS_FROM_CONFIG 26 | if (!projectRegistryAddress) { 27 | throw new Error(`No ProjectRegistryV1 found in chain "${CHAIN}", please supply it in env variable PROJECT_REGISTRY_ADDRESS`) 28 | } 29 | 30 | const interchainMailbox = chainToMailboxAddress(CHAIN) 31 | 32 | /** 33 | * npx hardhat run --network dev1 scripts/deployMarketplaceV4.ts 34 | * npx hardhat flatten contracts/MarketplaceV4.sol > mpv4.sol 35 | */ 36 | async function main() { 37 | log(`Deploying MarketplaceV4 to ${CHAIN}:`) 38 | log(` - project registry address: ${projectRegistryAddress}`) 39 | 40 | const projectRegistryFactory = await getContractFactory("ProjectRegistryV1") 41 | const projectRegistryFactoryTx = await projectRegistryFactory.attach(projectRegistryAddress) 42 | const projectRegistry = await projectRegistryFactoryTx.deployed() 43 | log("ProjectRegistryV1 attached at: ", projectRegistry.address) 44 | 45 | log(`Deploying MarketplaceV4 to ${CHAIN}:`) 46 | const Marketplace = await getContractFactory("MarketplaceV4") 47 | const marketplace = await upgrades.deployProxy(Marketplace, [projectRegistry.address, CHAIN_ID], { kind: 'uups' }) 48 | await marketplace.deployed() 49 | log(`MarketplaceV4 deployed on ${CHAIN} at: ${marketplace.address}`) 50 | 51 | try { 52 | await (await marketplace.addMailbox(interchainMailbox)).wait() 53 | log(`MarketplaceV4 added interchain mailbox: ${interchainMailbox}`) 54 | } catch (error) { 55 | log(`Error when setting interchain mailbox: ${error}`) 56 | } 57 | await projectRegistry.grantRole(id("TRUSTED_ROLE"), marketplace.address) 58 | log(`ProjectRegistry granted trusted role to MarketplaceV4.`) 59 | 60 | if (OUTPUT_FILE) { 61 | writeFileSync(OUTPUT_FILE, marketplace.address) 62 | log(`MarketplaceV4 address written to ${OUTPUT_FILE}`) 63 | } 64 | } 65 | 66 | main().catch((error) => { 67 | console.error(error) 68 | process.exitCode = 1 69 | }) 70 | -------------------------------------------------------------------------------- /packages/network-contracts/scripts/setTrusted.ts: -------------------------------------------------------------------------------- 1 | #!npx ts-node 2 | 3 | import { utils, Contract, providers, Wallet, Overrides } from "ethers" 4 | import { config } from "@streamr/config" 5 | 6 | import { streamRegistryABI } from "@streamr/network-contracts" 7 | import type { StreamRegistry } from "@streamr/network-contracts" 8 | 9 | const { log } = console 10 | 11 | const { isAddress, getAddress, parseUnits } = utils 12 | const { JsonRpcProvider } = providers 13 | 14 | const { 15 | CHAIN, 16 | KEY = "", 17 | GAS_PRICE_GWEI, 18 | 19 | TARGET = "", 20 | } = process.env 21 | 22 | if (!CHAIN) { 23 | throw new Error("Must set CHAIN environment variable, e.g. CHAIN=dev2") 24 | } 25 | if (!KEY) { 26 | throw new Error("Must set KEY environment variable to current admin's key, e.g. KEY=0x...") 27 | } 28 | 29 | const { 30 | contracts: { 31 | StreamRegistry: STREAM_REGISTRY_ADDRESS, 32 | }, 33 | rpcEndpoints: [{ url: ETHEREUM_RPC_URL }], 34 | blockExplorerUrl = "", 35 | } = (config as any)[CHAIN] 36 | 37 | const txOverrides: Overrides = {} 38 | if (GAS_PRICE_GWEI) { 39 | txOverrides.gasPrice = parseUnits(GAS_PRICE_GWEI, "gwei") 40 | } 41 | 42 | const lastArg = process.argv[process.argv.length - 1] 43 | const targetAddress = isAddress(lastArg) ? getAddress(lastArg) : isAddress(TARGET) ? getAddress(TARGET) : null 44 | if (targetAddress === null) { 45 | log("Target address can be given as command-line argument, or as TARGET environment variable.") 46 | throw new Error("Must give target address!") 47 | } 48 | 49 | async function main() { 50 | const provider = new JsonRpcProvider(ETHEREUM_RPC_URL) 51 | const currentAdmin = new Wallet(KEY, provider) 52 | await setTrusted(currentAdmin, targetAddress!) 53 | } 54 | 55 | async function setTrusted(currentAdminWallet: Wallet, targetAddress: string) { 56 | if (!STREAM_REGISTRY_ADDRESS) { 57 | throw new Error(`StreamRegistry must be set in the config! Not found in chain "${CHAIN}". 58 | Check CHAIN environment variable, or deploy StreamRegistry first.`) 59 | } 60 | const streamRegistry = new Contract(STREAM_REGISTRY_ADDRESS, streamRegistryABI, currentAdminWallet) as StreamRegistry 61 | if (!await streamRegistry.hasRole(await streamRegistry.DEFAULT_ADMIN_ROLE(), currentAdminWallet.address)) { 62 | throw new Error(`${currentAdminWallet.address} doesn't have StreamRegistry.DEFAULT_ADMIN_ROLE`) 63 | } 64 | log("Found StreamRegistry at %s", streamRegistry.address) 65 | 66 | const tr0 = await (await streamRegistry.grantRole(await streamRegistry.TRUSTED_ROLE(), targetAddress, txOverrides)).wait() 67 | log("Granted StreamRegistry.TRUSTED_ROLE to %s (%s/tx/%s )", targetAddress, blockExplorerUrl, tr0.transactionHash) 68 | } 69 | 70 | if (require.main === module) { 71 | main().catch(console.error) 72 | } 73 | -------------------------------------------------------------------------------- /packages/network-contracts/contracts/OperatorTokenomics/OperatorPolicies/IExchangeRatePolicy.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.13; 4 | 5 | interface IExchangeRatePolicy { 6 | function setParam(uint param) external; 7 | 8 | /** 9 | * Conversion from Operator's internal token to DATA when undelegating 10 | * We calculate using valueWithoutEarnings() because smart contract's can't check the outstanding earnings of arbitrary number of Sponsorships 11 | * @dev specifically, dataWei should be the *highest* amount of DATA that can be received from undelegating operatorTokenWei 12 | * @param operatorTokenWei Amount of Operator's internal token to undelegate 13 | * @return dataWei Amount of DATA token that would be received from the undelegation 14 | */ 15 | function operatorTokenToData(uint operatorTokenWei) external view returns (uint dataWei); 16 | 17 | /** 18 | * (Inverse) conversion from DATA to Operator's internal token when undelegating 19 | * We calculate using valueWithoutEarnings() because smart contract's can't check the outstanding earnings of arbitrary number of Sponsorships 20 | * @dev specifically, operatorTokenWei should be the *least* amount that must be undelegated to receive dataWei 21 | * @dev for bijective conversion functions, this MUST be the inverse of operatorTokenToData() 22 | * @param dataWei Amount of DATA we want from undelegating 23 | * @return operatorTokenWei Amount of Operator's internal token to undelegate to receive the given amount of DATA 24 | */ 25 | function operatorTokenToDataInverse(uint dataWei) external view returns (uint operatorTokenWei); 26 | 27 | /** 28 | * Conversion from DATA to Operator's internal token when delegating 29 | * We calculate using valueWithoutEarnings() because smart contract's can't check the outstanding earnings of arbitrary number of Sponsorships 30 | * First delegation should be the operator's self-delegation. For that, use 1:1 exchange rate. 31 | * NOTE: this function can be called after the DATA tokens have actually been transferred (which is typically the case), 32 | * the exchange rate is just adjusted accordingly by subtracting alreadyTransferredWei from the Operator value 33 | * @dev specifically, operatorTokenWei should be the *highest* amount of DATA that can be received from delegating dataWei 34 | * @param dataWei Amount of DATA token to delegate 35 | * @param alreadyTransferredWei Amount of DATA to subtract from Operator value for calculations (because DATA balance already was incremented but calculation needs the pre-increment value) 36 | * @return operatorTokenWei Amount of Operator's internal token that would be received from the delegation 37 | **/ 38 | function dataToOperatorToken(uint dataWei, uint alreadyTransferredWei) external view returns (uint operatorTokenWei); 39 | } 40 | -------------------------------------------------------------------------------- /packages/network-contracts/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "command": "npm run e2etest", 6 | "name": "Run npm run e2etest", 7 | "request": "launch", 8 | "type": "node-terminal" 9 | }, 10 | { 11 | "name": "Launch via NPM", 12 | "request": "launch", 13 | "runtimeArgs": [ 14 | "run", 15 | "test" 16 | ], 17 | "runtimeExecutable": "npm", 18 | "skipFiles": [ 19 | "/**" 20 | ], 21 | "type": "pwa-node" 22 | }, 23 | { 24 | "name": "deploy", 25 | "request": "launch", 26 | "runtimeArgs": [ 27 | "run", 28 | "deploylive" 29 | ], 30 | "runtimeExecutable": "npm", 31 | "skipFiles": [ 32 | "/**" 33 | ], 34 | "type": "pwa-node" 35 | }, 36 | { 37 | "name": "migrate", 38 | "request": "launch", 39 | "runtimeArgs": [ 40 | "run", 41 | "migrateStreams" 42 | ], 43 | "runtimeExecutable": "npm", 44 | "skipFiles": [ 45 | "/**" 46 | ], 47 | "type": "pwa-node" 48 | }, 49 | { 50 | "name": "cleanup", 51 | "request": "launch", 52 | "runtimeArgs": [ 53 | "run", 54 | "cleanupStreams" 55 | ], 56 | "runtimeExecutable": "npm", 57 | "skipFiles": [ 58 | "/**" 59 | ], 60 | "type": "pwa-node" 61 | }, 62 | { 63 | "type": "node", 64 | "request": "launch", 65 | "name": "Hardhat test this file", 66 | "runtimeExecutable": "${workspaceFolder}/node_modules/.bin/hardhat", 67 | "env": { 68 | "DEBUG": "*,-mocha:*" 69 | }, 70 | "runtimeArgs": [ 71 | "test", 72 | "${file}" 73 | ], 74 | "internalConsoleOptions": "openOnSessionStart", 75 | "cwd": "${workspaceFolder}" 76 | }, 77 | { 78 | "type": "node", 79 | "request": "launch", 80 | "name": "Hardhat run this file", 81 | "runtimeExecutable": "${workspaceFolder}/node_modules/.bin/hardhat", 82 | "env": { 83 | "DEBUG": "*,-mocha:*" 84 | }, 85 | "runtimeArgs": [ 86 | "run", 87 | "--network", 88 | "dev1", 89 | "${file}" 90 | ], 91 | "internalConsoleOptions": "openOnSessionStart", 92 | "cwd": "${workspaceFolder}" 93 | } 94 | ] 95 | } -------------------------------------------------------------------------------- /packages/network-contracts/scripts/registerStorageNode.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 | 9 | import type { StreamRegistry, NodeRegistry, StreamStorageRegistry } from "@streamr/network-contracts" 10 | 11 | import { address as streamRegistryAddress, abi as streamRegistryAbi } from "../deployments/localsidechain/StreamRegistry.json" 12 | import { address as nodeRegistryAddress, abi as nodeRegistryAbi } from "../deployments/localsidechain/NodeRegistry.json" 13 | import { address as ssRegistryAddress, abi as ssRegistryAbi } from "../deployments/localsidechain/StreamStorageRegistry.json" 14 | 15 | const SIDECHAINURL = "http://localhost:8546" 16 | const DEFAULTPRIVATEKEY = "0x5e98cce00cff5dea6b454889f359a4ec06b9fa6b88e9d69b86de8e1c81887da0" 17 | 18 | const provider = new JsonRpcProvider(SIDECHAINURL) 19 | const wallet = new Wallet(DEFAULTPRIVATEKEY, provider) 20 | const node = Wallet.createRandom() 21 | 22 | async function main() { 23 | const streamReg = new Contract(streamRegistryAddress, streamRegistryAbi, wallet) as StreamRegistry 24 | const nodeReg = new Contract(nodeRegistryAddress, nodeRegistryAbi, wallet) as NodeRegistry 25 | const ssReg = new Contract(ssRegistryAddress, ssRegistryAbi, wallet) as StreamStorageRegistry 26 | 27 | const path = `/test-${Date.now()}` 28 | const streamId = wallet.address.toLowerCase() + path 29 | const tx1 = await streamReg.createStream(path, "{\"partitions\":1}") 30 | const tr1 = await tx1.wait() 31 | console.log("Stream %s registered, tx: %s", streamId, tr1.transactionHash) 32 | 33 | const tx2 = await nodeReg.createOrUpdateNode(node.address, "http://node.url") 34 | const tr2 = await tx2.wait() 35 | console.log("Node %s registered, tx: %s", node.address, tr2.transactionHash) 36 | 37 | // const metadata = await streamReg.streamIdToMetadata(streamId) 38 | // console.log("metadata", metadata) 39 | // const nodeInfo = await nodeReg.getNode(node.address) 40 | // console.log("node info", nodeInfo) 41 | // const perms = await streamReg.getPermissionsForUser(streamId, wallet.address) 42 | // console.log("perms", perms) 43 | // enum PermissionType { Edit, Delete, Publish, Subscribe, Share } 44 | // const canEdit = await streamReg.hasPermission(streamId, wallet.address, PermissionType.Edit) 45 | // console.log("canEdit", canEdit) 46 | 47 | const tx3 = await ssReg.addStorageNode(streamId, node.address, { gasLimit: "1000000" }) 48 | const tr3 = await tx3.wait() 49 | console.log("Storage node %s added to stream %s, tx: %s", node.address, streamId, tr3.transactionHash) 50 | } 51 | 52 | main() 53 | .then(() => process.exit(0)) 54 | .catch((error) => { 55 | console.error(error) 56 | process.exit(1) 57 | }) 58 | 59 | -------------------------------------------------------------------------------- /packages/network-contracts/logs/2024-03-07-temp-low-minimumstake.txt: -------------------------------------------------------------------------------- 1 | $ ./scripts/2024-03-07-temp-low-minimumstake.sh 2 | 3 | [[ the 1G early leaver penalty was set earlier ]] 4 | 5 | + export CHAIN=polygon 6 | + CHAIN=polygon 7 | + export GAS_PRICE_GWEI=150 8 | + GAS_PRICE_GWEI=150 9 | + ./scripts/streamrConfig.ts setFlaggerRewardWei 0.003 10 | Wallet address used: 0x41e36D4fFb5B443B20f55bcFf27c68fF086Fe06f 11 | Checking network polygon: https://polygon-rpc.com 12 | {"name":"matic","chainId":137,"ensAddress":null} 13 | Checking StreamrConfig at 0x869e88dB146ECAF20dDf199a12684cD80c263c8f 14 | 0x3b49a237fe2d18fa4d9642b8a0e065923cceb71b797783b619a030a61d848bf0 [OK] 15 | Current value of flaggerRewardWei: 36000000000000000000 (36.0) 16 | Setting setFlaggerRewardWei to 3000000000000000 (0.003), overrides: {"gasPrice":{"type":"BigNumber","hex":"0x22ecb25c00"}} 17 | Transaction: https://polygonscan.com/tx/0x6c904c584d52ff85ca6f0bdf3fe4ad095e170f0d97f9c1ea9253f07b9dd8c486 18 | Transaction receipt: { 19 | blockNumber: 54366623, 20 | from: '0x41e36D4fFb5B443B20f55bcFf27c68fF086Fe06f', 21 | to: '0x869e88dB146ECAF20dDf199a12684cD80c263c8f', 22 | transactionHash: '0x6c904c584d52ff85ca6f0bdf3fe4ad095e170f0d97f9c1ea9253f07b9dd8c486', 23 | events: [ 24 | { event: 'ConfigChanged', args: {} }, 25 | { 26 | event: 'unknown', 27 | args: {}, 28 | address: '0x0000000000000000000000000000000000001010' 29 | }, 30 | [length]: 2 31 | ] 32 | } 33 | + ./scripts/streamrConfig.ts setFlagReviewerRewardWei 0.001 34 | Wallet address used: 0x41e36D4fFb5B443B20f55bcFf27c68fF086Fe06f 35 | Checking network polygon: https://polygon-rpc.com 36 | {"name":"matic","chainId":137,"ensAddress":null} 37 | Checking StreamrConfig at 0x869e88dB146ECAF20dDf199a12684cD80c263c8f 38 | 0x3b49a237fe2d18fa4d9642b8a0e065923cceb71b797783b619a030a61d848bf0 [OK] 39 | Current value of flagReviewerRewardWei: 2000000000000000000 (2.0) 40 | Setting setFlagReviewerRewardWei to 1000000000000000 (0.001), overrides: {"gasPrice":{"type":"BigNumber","hex":"0x22ecb25c00"}} 41 | Transaction: https://polygonscan.com/tx/0x9389a66e7cf5cd56c8f9c72208bd5744c43cc170c0ad2bbae23857181509786c 42 | Transaction receipt: { 43 | blockNumber: 54366633, 44 | from: '0x41e36D4fFb5B443B20f55bcFf27c68fF086Fe06f', 45 | to: '0x869e88dB146ECAF20dDf199a12684cD80c263c8f', 46 | transactionHash: '0x9389a66e7cf5cd56c8f9c72208bd5744c43cc170c0ad2bbae23857181509786c', 47 | events: [ 48 | { event: 'ConfigChanged', args: {} }, 49 | { 50 | event: 'unknown', 51 | args: {}, 52 | address: '0x0000000000000000000000000000000000001010' 53 | }, 54 | [length]: 2 55 | ] 56 | } 57 | + ./scripts/streamrConfig.ts minimumStakeWei 58 | Wallet address used: 0x41e36D4fFb5B443B20f55bcFf27c68fF086Fe06f 59 | Checking network polygon: https://polygon-rpc.com 60 | {"name":"matic","chainId":137,"ensAddress":null} 61 | Checking StreamrConfig at 0x869e88dB146ECAF20dDf199a12684cD80c263c8f 62 | 0x3b49a237fe2d18fa4d9642b8a0e065923cceb71b797783b619a030a61d848bf0 [OK] 63 | minimumStakeWei: 1000000000000000000 (1.0) 64 | -------------------------------------------------------------------------------- /packages/network-contracts/contracts/OperatorTokenomics/OperatorPolicies/DefaultUndelegationPolicy.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.13; 4 | 5 | import "./IUndelegationPolicy.sol"; 6 | import "../StreamrConfigV1_1.sol"; 7 | import "../Operator.sol"; 8 | 9 | contract DefaultUndelegationPolicy is Operator, IUndelegationPolicy { 10 | 11 | function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { 12 | return interfaceId == type(IUndelegationPolicy).interfaceId; 13 | } 14 | 15 | function setParam(uint) external { 16 | 17 | } 18 | 19 | /** 20 | * Check the operator's self-undelegation limit i.e. how much of the Operator token supply does the operator have as "skin in the game". 21 | * For others, it's always OK to undelegate. 22 | * After self-undelegation, operator must still hold at least minimumSelfDelegationFraction of the total supply. 23 | * @dev NOTE that amount can be anything, including more than the actual delegated DATA balance. 24 | * @dev If there's lots of undelegation queue, those tokens still count for the totalSupply. 25 | * @dev This means if the queue is left un-serviced, the operator's effective self-delegation limit is higher. 26 | * @dev Minimum delegation limit isn't checked here because this check happens while queueing. 27 | * @dev Minimum delegation is handled when paying out the queue, and checked separately on _transfer. 28 | **/ 29 | function onUndelegate(address delegator, uint amountDataWei) external { 30 | StreamrConfigV1_1 config = StreamrConfigV1_1(address(streamrConfig)); 31 | if (delegator == owner) { 32 | // if all has been unstaked, no slashing can be coming that requires self-stake => allow self-undelegation ("rapid shutdown") 33 | // otherwise the operator would have to wait for all delegators to undelegate first 34 | if (totalStakedIntoSponsorshipsWei == 0) { return; } 35 | 36 | uint amountOperatorTokens = moduleCall(address(exchangeRatePolicy), abi.encodeWithSelector(exchangeRatePolicy.operatorTokenToDataInverse.selector, amountDataWei)); 37 | uint actualAmount = min(amountOperatorTokens, balanceOf(owner)); 38 | uint balanceAfter = balanceOf(owner) - actualAmount; 39 | uint totalSupplyAfter = totalSupply() - actualAmount; 40 | uint minimumFraction = 0; 41 | try config.minimumSelfDelegationFraction() returns (uint f) { 42 | minimumFraction = f; 43 | } catch {} 44 | require(1 ether * balanceAfter >= totalSupplyAfter * minimumFraction, "error_selfDelegationTooLow"); 45 | } else { 46 | uint minimumSeconds = 0; 47 | try config.minimumDelegationSeconds() returns (uint s) { 48 | minimumSeconds = s; 49 | } catch {} 50 | // solhint-disable-next-line not-rely-on-time 51 | require(block.timestamp > latestDelegationTimestamp[delegator] + minimumSeconds, "error_undelegateTooSoon"); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /packages/dev-chain-fast/hardhat.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 3 | defaultNetwork: 'hardhat', 4 | networks: { 5 | hardhat: { 6 | initialDate: "2025-01-01T00:00:00Z", // deploy.ts will set block timestamp to wall clock time after it's done 7 | gas: 12000000, 8 | blockGasLimit: 0x1fffffffffffff, 9 | allowUnlimitedContractSize: true, 10 | mining: { 11 | // TODO: consider setting auto to false 12 | // pro: in real setting, many tx can go into the same block, with automine every tx gets its own block 13 | // pro: with auto:true, if tx are sent very fast, since each block must increment by at least 1 second 14 | // it may cause block timestamps to drift forward from the wall clock time; auto:false would fix this 15 | // con: tests will probably run slower 16 | auto: true, 17 | interval: 1000 18 | }, 19 | accounts: [ 20 | { "privateKey": "0x5e98cce00cff5dea6b454889f359a4ec06b9fa6b88e9d69b86de8e1c81887da0", "balance": "100000000000000000000000000" }, 21 | { "privateKey": "0xe5af7834455b7239881b85be89d905d6881dcb4751063897f12be1b0dd546bdb", "balance": "100000000000000000000000000" }, 22 | { "privateKey": "0x4059de411f15511a85ce332e7a428f36492ab4e87c7830099dadbf130f1896ae", "balance": "100000000000000000000000000" }, 23 | { "privateKey": "0x633a182fb8975f22aaad41e9008cb49a432e9fdfef37f151e9e7c54e96258ef9", "balance": "100000000000000000000000000" }, 24 | { "privateKey": "0x957a8212980a9a39bf7c03dcbeea3c722d66f2b359c669feceb0e3ba8209a297", "balance": "100000000000000000000000000" }, 25 | { "privateKey": "0xfe1d528b7e204a5bdfb7668a1ed3adfee45b4b96960a175c9ef0ad16dd58d728", "balance": "100000000000000000000000000" }, 26 | { "privateKey": "0xd7609ae3a29375768fac8bc0f8c2f6ac81c5f2ffca2b981e6cf15460f01efe14", "balance": "100000000000000000000000000" }, 27 | { "privateKey": "0xb1abdb742d3924a45b0a54f780f0f21b9d9283b231a0a0b35ce5e455fa5375e7", "balance": "100000000000000000000000000" }, 28 | { "privateKey": "0x2cd9855d17e01ce041953829398af7e48b24ece04ff9d0e183414de54dc52285", "balance": "100000000000000000000000000" }, 29 | { "privateKey": "0xaa7a3b3bb9b4a662e756e978ad8c6464412e7eef1b871f19e5120d4747bce966", "balance": "100000000000000000000000000" }, 30 | { "privateKey": "0xa51ef2b7a1f160e53cc3a17c5a74ea0563a50030644e9f6e1b1f4a7d0afd088e", "balance": "100000000000000000000000000" }, 31 | { "privateKey": "0x2ba0e218041e81b8e5c88ee4134995c1e358c2c01664676d975792057c57e333", "balance": "100000000000000000000000000" } 32 | ] 33 | }, 34 | }, 35 | solidity: { 36 | compilers: [ 37 | { 38 | version: '0.8.13', 39 | settings: { 40 | optimizer: { 41 | enabled: true, 42 | runs: 100, 43 | }, 44 | }, 45 | }, 46 | ], 47 | } 48 | } -------------------------------------------------------------------------------- /packages/network-contracts/contracts/OperatorTokenomics/OperatorPolicies/NodeModule.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.13; 4 | 5 | import "./INodeModule.sol"; 6 | import "../StreamrConfig.sol"; 7 | import "../Operator.sol"; 8 | 9 | contract NodeModule is INodeModule, Operator { 10 | mapping (address => bool) private isInNewNodes; // lookup used during the setNodeAddresses 11 | 12 | function createCoordinationStream() external { 13 | streamRegistry = IStreamRegistryV4(streamrConfig.streamRegistryAddress()); 14 | streamId = string.concat(streamRegistry.addressToString(address(this)), "/operator/coordination"); 15 | streamRegistry.createStream("/operator/coordination", "{\"partitions\":1}"); 16 | streamRegistry.grantPublicPermission(streamId, IStreamRegistryV4.PermissionType.Subscribe); 17 | } 18 | 19 | function _setNodeAddresses(address[] calldata newNodes) external { 20 | // add new nodes on top 21 | for (uint i; i < newNodes.length; i++) { 22 | address node = newNodes[i]; 23 | if (nodeIndex[node] == 0) { 24 | _addNode(node); 25 | } 26 | isInNewNodes[node] = true; 27 | } 28 | // remove from old nodes 29 | for (uint i; i < nodes.length;) { 30 | address node = nodes[i]; 31 | if (!isInNewNodes[node]) { 32 | _removeNode(node); 33 | } else { 34 | i++; 35 | } 36 | } 37 | // reset lookup (TODO: replace with transient storage once https://eips.ethereum.org/EIPS/eip-1153 is available) 38 | for (uint i; i < newNodes.length; i++) { 39 | address node = newNodes[i]; 40 | delete isInNewNodes[node]; 41 | } 42 | emit NodesSet(nodes); 43 | } 44 | 45 | /** First add then remove addresses (if in both lists, ends up removed!) */ 46 | function _updateNodeAddresses(address[] calldata addNodes, address[] calldata removeNodes) external { 47 | for (uint i; i < addNodes.length; i++) { 48 | address node = addNodes[i]; 49 | if (nodeIndex[node] == 0) { 50 | _addNode(node); 51 | } 52 | } 53 | for (uint i; i < removeNodes.length; i++) { 54 | address node = removeNodes[i]; 55 | if (nodeIndex[node] > 0) { 56 | _removeNode(node); 57 | } 58 | } 59 | emit NodesSet(nodes); 60 | } 61 | 62 | function _addNode(address node) internal { 63 | nodes.push(node); 64 | nodeIndex[node] = nodes.length; // will be +1 65 | 66 | streamRegistry.grantPermission(streamId, node, IStreamRegistryV4.PermissionType.Publish); 67 | } 68 | 69 | function _removeNode(address node) internal { 70 | uint index = nodeIndex[node] - 1; 71 | address lastNode = nodes[nodes.length - 1]; 72 | nodes[index] = lastNode; 73 | nodes.pop(); 74 | nodeIndex[lastNode] = index + 1; 75 | delete nodeIndex[node]; 76 | 77 | streamRegistry.revokePermission(streamId, node, IStreamRegistryV4.PermissionType.Publish); 78 | } 79 | } 80 | --------------------------------------------------------------------------------