├── scripts ├── slither-results │ └── .gitkeep ├── slither.ts └── setup-tutorial.ts ├── .prettierrc ├── .github ├── CODEOWNERS ├── workflows │ ├── semantic-pr.yaml │ ├── lint.yaml │ ├── test.yaml │ ├── gitguardian.yml │ ├── slither.yaml │ └── publish-npm.yaml └── dependabot.yml ├── packages ├── zeta-app-contracts │ ├── arguments.js │ ├── contracts │ │ ├── shared │ │ │ ├── ImmutableCreate2Factory.sol │ │ │ ├── IWZeta.sol │ │ │ └── ZetaEthMock.sol │ │ └── multi-chain-value │ │ │ └── test │ │ │ ├── MultiChainValueMock.sol │ │ │ └── ZetaConnectorMock.sol │ ├── tsconfig.json │ ├── .env.example │ ├── README.md │ ├── .eslintrc.js │ ├── scripts │ │ ├── multi-chain-value │ │ │ ├── verify-contract.ts │ │ │ ├── deterministic-deploy.ts │ │ │ ├── deploy.ts │ │ │ ├── search-reverted-tx.ts │ │ │ ├── send-value.ts │ │ │ └── search-zbtc-tx-cache.ts │ │ └── address.helpers.ts │ ├── lib │ │ ├── shared │ │ │ ├── deploy.helpers.ts │ │ │ └── ImmutableCreate2Factory.helpers.ts │ │ └── multi-chain-value │ │ │ └── MultiChainValue.helpers.ts │ ├── package.json │ ├── data │ │ └── addresses.json │ ├── docs │ │ └── multi-chain-value.md │ └── hardhat.config.ts └── zevm-app-contracts │ ├── arguments.js │ ├── .env.example │ ├── contracts │ ├── shared │ │ ├── TestContracts.sol │ │ ├── TestUniswapContracts.sol │ │ ├── BytesHelperLib.sol │ │ ├── MockZRC20.sol │ │ ├── MockSystemContract.sol │ │ └── SwapHelperLib.sol │ ├── xp-nft │ │ ├── test │ │ │ └── xpNFTV2.sol │ │ ├── xpNFT_V3.sol │ │ └── xpNFT_V2.sol │ ├── liquidity-incentives │ │ ├── Synthetixio │ │ │ ├── RewardsDistributionRecipient.sol │ │ │ ├── interfaces │ │ │ │ └── IStakingRewards.sol │ │ │ ├── Owned.sol │ │ │ └── Pausable.sol │ │ └── RewardDistributorFactory.sol │ ├── withdrawErc20 │ │ └── withdrawErc20.sol │ ├── zeta-swap │ │ ├── ZetaSwap.sol │ │ ├── ZetaSwapBtcInbound.sol │ │ └── ZetaCurveSwapDemo.sol │ ├── instant-rewards │ │ ├── InstantRewardsFactory.sol │ │ ├── InstantRewardsV2.sol │ │ └── InstantRewards.sol │ ├── disperse │ │ └── Disperse.sol │ ├── zeta-points │ │ └── InvitationManagerV2.sol │ ├── multi-output │ │ └── ZetaMultiOutput.sol │ └── proof-of-liveness │ │ └── ProofOfLiveness.sol │ ├── tsconfig.json │ ├── README.md │ ├── .eslintrc.js │ ├── test │ ├── zeta-points │ │ ├── test.helpers.ts │ │ ├── invitationManager.helpers.ts │ │ └── UserVerificationRegistry.ts │ ├── instant-rewards │ │ ├── test.helpers.ts │ │ └── instant-rewards-v2.ts │ ├── Owned.spec.ts │ ├── Synthetixio │ │ └── common.js │ ├── xp-nft │ │ ├── test.helpers.ts │ │ └── xp-nft-v2.ts │ ├── test.helpers.ts │ ├── Swap.spec.ts │ └── proof-of-liveness │ │ └── proof-of-liveness.ts │ ├── scripts │ ├── explorer.helpers.ts │ ├── zeta-swap │ │ ├── send.ts │ │ ├── swap.ts │ │ ├── deploy.ts │ │ ├── get-system-data.ts │ │ ├── helpers.ts │ │ ├── swap-on-zevm.ts │ │ └── stress-swap.ts │ ├── disperse │ │ └── deploy.ts │ ├── withdrawERC20 │ │ ├── deploy.ts │ │ └── withdraw.ts │ ├── zeta-points │ │ ├── deploy.ts │ │ └── deploy-invitationV2.ts │ ├── proof-of-liveness │ │ └── deploy.ts │ ├── xp-nft │ │ ├── upgrade-v2.ts │ │ ├── gov-query-proposals.ts │ │ ├── deploy.ts │ │ ├── gov-set-level.ts │ │ └── deploy-gov.ts │ ├── liquidity-incentives │ │ ├── add-reward.ts │ │ ├── deploy.ts │ │ ├── deploy-token-reward.ts │ │ ├── deploy-zrc20-reward.ts │ │ ├── deploy-default-rewards.ts │ │ ├── read-rewards-data.ts │ │ └── helpers.ts │ ├── instant-rewards │ │ ├── deploy.ts │ │ └── deploy-v2.ts │ ├── address.helpers.ts │ └── uniswap │ │ ├── uniswap.helpers.ts │ │ ├── sell-zeta.ts │ │ ├── sell-token.ts │ │ └── remove-liquidity-zeta-uniswap.ts │ ├── data │ └── addresses.json │ ├── package.json │ └── hardhat.config.ts ├── .eslintignore ├── .prettierignore ├── .dockerignore ├── .vscode ├── extensions.json └── settings.json ├── .yarnrc.yml ├── slither.config.json ├── .solhint.json ├── .gitignore ├── tsconfig.json ├── Dockerfile ├── CONTRIBUTING.md ├── LICENSE ├── .eslintrc.js ├── README.md ├── bugbounty.md └── package.json /scripts/slither-results/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 120 3 | } 4 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @andresaiello @fadeev @gmaiolo @owentar 2 | -------------------------------------------------------------------------------- /packages/zeta-app-contracts/arguments.js: -------------------------------------------------------------------------------- 1 | module.exports = []; 2 | -------------------------------------------------------------------------------- /packages/zevm-app-contracts/arguments.js: -------------------------------------------------------------------------------- 1 | module.exports = []; 2 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | .yarn 2 | artifacts 3 | cache 4 | coverage 5 | node_modules 6 | typechain-types 7 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | .yarn 2 | artifacts 3 | cache 4 | coverage* 5 | gasReporterOutput.json 6 | node_modules 7 | -------------------------------------------------------------------------------- /packages/zevm-app-contracts/.env.example: -------------------------------------------------------------------------------- 1 | ALCHEMY_API_KEY_MAINNET= 2 | ALCHEMY_API_KEY_GOERLI= -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | **/node_modules/ 2 | **/cache 3 | **/.env 4 | Dockerfile 5 | .github 6 | .vscode 7 | .changeset 8 | .dockerignore 9 | .secrets -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "dbaeumer.vscode-eslint", 4 | "NomicFoundation.hardhat-solidity" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /.yarnrc.yml: -------------------------------------------------------------------------------- 1 | nodeLinker: node-modules 2 | 3 | plugins: 4 | - path: .yarn/plugins/@yarnpkg/plugin-workspace-tools.cjs 5 | spec: "@yarnpkg/plugin-workspace-tools" 6 | 7 | yarnPath: .yarn/releases/yarn-3.2.0.cjs 8 | 9 | nmHoistingLimits: workspaces 10 | -------------------------------------------------------------------------------- /packages/zevm-app-contracts/contracts/shared/TestContracts.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.7; 3 | 4 | /** 5 | * @dev Contracts that need to be compiled for testing purposes 6 | */ 7 | 8 | import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; 9 | -------------------------------------------------------------------------------- /slither.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "detectors_to_exclude": "", 3 | "compile_force_framework": "hardhat", 4 | "hardhat_ignore_compile": true, 5 | "npx_disable": true, 6 | "filter_paths": "artifacts,cache,data,dist,docs,lib,node_modules,pkg,scripts,tasks,test,testing,typechain-types" 7 | } 8 | -------------------------------------------------------------------------------- /packages/zeta-app-contracts/contracts/shared/ImmutableCreate2Factory.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.5.10; 3 | 4 | /** 5 | * @dev Contracts that need to be compiled for testing purposes 6 | */ 7 | 8 | import "@zetachain/protocol-contracts/contracts/evm/tools/ImmutableCreate2Factory.sol"; 9 | -------------------------------------------------------------------------------- /packages/zevm-app-contracts/contracts/xp-nft/test/xpNFTV2.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.20; 3 | 4 | import "../xpNFT.sol"; 5 | 6 | contract ZetaXPV2 is ZetaXP { 7 | function version() public pure override returns (string memory) { 8 | return "1.0.1"; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /.solhint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "solhint:recommended", 3 | "rules": { 4 | "compiler-version": ["error", "0.8.7"], 5 | "func-visibility": ["warn", { "ignoreConstructors": true }], 6 | "reason-string": ["warn", { "maxLength": 80 }], 7 | "no-empty-blocks": "off", 8 | "check-send-result": "off" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /packages/zeta-app-contracts/contracts/multi-chain-value/test/MultiChainValueMock.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.7; 3 | 4 | import "../MultiChainValue.sol"; 5 | 6 | contract MultiChainValueMock is MultiChainValue { 7 | constructor(address connectorAddress, address _zetaTokenInput) MultiChainValue(connectorAddress, _zetaTokenInput) {} 8 | } 9 | -------------------------------------------------------------------------------- /.github/workflows/semantic-pr.yaml: -------------------------------------------------------------------------------- 1 | name: "Semantic PR" 2 | on: 3 | pull_request_target: 4 | types: 5 | - opened 6 | - edited 7 | - synchronize 8 | 9 | jobs: 10 | main: 11 | name: Validate PR title 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: amannn/action-semantic-pull-request@v5 15 | env: 16 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 17 | -------------------------------------------------------------------------------- /packages/zeta-app-contracts/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "include": [ 4 | "./scripts", 5 | "./lib", 6 | "./test", 7 | "./tests", 8 | "./typechain", 9 | "./**/*.json", 10 | "typechain-types" 11 | ], 12 | "exclude": [ 13 | "artifacts", 14 | "cache", 15 | "hardhat.config.ts", 16 | ], 17 | "compilerOptions": { 18 | "baseUrl": "." 19 | } 20 | } -------------------------------------------------------------------------------- /packages/zevm-app-contracts/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "include": [ 4 | "./scripts", 5 | "./lib", 6 | "./test", 7 | "./tests", 8 | "./typechain", 9 | "./**/*.json", 10 | "typechain-types" 11 | ], 12 | "exclude": [ 13 | "artifacts", 14 | "cache", 15 | "hardhat.config.ts", 16 | ], 17 | "compilerOptions": { 18 | "baseUrl": ".", 19 | } 20 | } -------------------------------------------------------------------------------- /packages/zevm-app-contracts/README.md: -------------------------------------------------------------------------------- 1 | # ZetaEVM App contracts 2 | 3 | > ⚠️ The contracts **are not audited yet**, use at your own risk. 4 | 5 | ## Environment variables 6 | 7 | Follow the template in `.env.example`. 8 | 9 | * The private key should **not** include `0x`. 10 | * To verify your contracts, you'll need api keys both in BSCScan and Etherscan. 11 | 12 | ## Running Tests 13 | 14 | ```bash 15 | yarn test 16 | ``` 17 | -------------------------------------------------------------------------------- /packages/zevm-app-contracts/contracts/shared/TestUniswapContracts.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.6.6; 3 | 4 | /** 5 | * @dev Contracts that need to be compiled for testing purposes 6 | */ 7 | 8 | import "@uniswap/v2-core/contracts/interfaces/IUniswapV2Factory.sol"; 9 | import "@uniswap/v2-core/contracts/interfaces/IUniswapV2Pair.sol"; 10 | import "@uniswap/v2-periphery/contracts/UniswapV2Router02.sol"; 11 | -------------------------------------------------------------------------------- /packages/zeta-app-contracts/.env.example: -------------------------------------------------------------------------------- 1 | ALCHEMY_API_KEY_MAINNET= 2 | ALCHEMY_API_KEY_GOERLI= 3 | 4 | PUBLIC_KEY_1= 5 | 6 | PRIVATE_KEY= 7 | 8 | # ETHER 9 | ETHERSCAN_API_KEY=<...> 10 | 11 | # POLYGON 12 | POYLGONSCAN_API_KEY=<...> 13 | 14 | # BSC 15 | BSCSCAN_API_KEY=<...> 16 | 17 | # Deployer address. This is needed to calculate salt in deterministic deployments 18 | DEPLOYER_ADDRESS= 19 | -------------------------------------------------------------------------------- /packages/zeta-app-contracts/README.md: -------------------------------------------------------------------------------- 1 | # ZetaChain App contracts 2 | 3 | This package include the contracts used by different ZetaChain Apps like ZetaHub 4 | 5 | ## Environment variables 6 | 7 | Follow the template in `.env.example`. 8 | 9 | * The private key should **not** include `0x`. 10 | * To verify your contracts, you'll need api keys both in BSCScan and Etherscan. 11 | 12 | ## Running Tests 13 | 14 | ```bash 15 | yarn test 16 | ``` 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Yarn 2 | node_modules 3 | .pnp.* 4 | .yarn/* 5 | !.yarn/patches 6 | !.yarn/plugins 7 | !.yarn/releases 8 | !.yarn/sdks 9 | !.yarn/versions 10 | 11 | 12 | # Hardhat 13 | artifacts 14 | typechain-types 15 | cache 16 | 17 | # Typescript 18 | dist 19 | tsconfig.tsbuildinfo 20 | 21 | # Misc 22 | .env 23 | .DS_Store 24 | .secrets 25 | .wallet.json 26 | 27 | # Slither 28 | scripts/slither-results/* 29 | !scripts/slither-results/.gitkeep 30 | 31 | **/coverage/* 32 | **/coverage.json 33 | -------------------------------------------------------------------------------- /packages/zeta-app-contracts/.eslintrc.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | 3 | const OFF = 0; 4 | 5 | module.exports = { 6 | env: { 7 | browser: false, 8 | es2021: true, 9 | mocha: true, 10 | node: true, 11 | }, 12 | extends: ["../../.eslintrc.js"], 13 | rules: { 14 | "no-console": OFF, 15 | }, 16 | settings: { 17 | "import/resolver": { 18 | typescript: { 19 | project: path.join(__dirname, "tsconfig.json"), 20 | }, 21 | }, 22 | }, 23 | }; 24 | -------------------------------------------------------------------------------- /packages/zevm-app-contracts/.eslintrc.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | 3 | const OFF = 0; 4 | 5 | module.exports = { 6 | env: { 7 | browser: false, 8 | es2021: true, 9 | mocha: true, 10 | node: true, 11 | }, 12 | extends: ["../../.eslintrc.js"], 13 | rules: { 14 | "no-console": OFF, 15 | }, 16 | settings: { 17 | "import/resolver": { 18 | typescript: { 19 | project: path.join(__dirname, "tsconfig.json"), 20 | }, 21 | }, 22 | }, 23 | }; 24 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "npm" 4 | directories: 5 | - "/" 6 | - "/packages/zeta-app-contracts" 7 | - "/packages/zevm-app-contracts" 8 | schedule: 9 | interval: "daily" 10 | commit-message: 11 | prefix: "dependabot: " 12 | labels: 13 | - "dependencies" 14 | allow: 15 | - dependency-name: "@zetachain/toolkit" 16 | - dependency-name: "@zetachain/networks" 17 | - dependency-name: "@zetachain/protocol-contracts" 18 | -------------------------------------------------------------------------------- /packages/zeta-app-contracts/contracts/shared/IWZeta.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.7; 3 | 4 | interface IWZeta { 5 | function deposit() external payable; 6 | 7 | function withdraw(uint wad) external; 8 | 9 | function totalSupply() external view returns (uint); 10 | 11 | function approve(address guy, uint wad) external returns (bool); 12 | 13 | function transfer(address dst, uint wad) external returns (bool); 14 | 15 | function transferFrom(address src, address dst, uint wad) external returns (bool); 16 | } 17 | -------------------------------------------------------------------------------- /.github/workflows/lint.yaml: -------------------------------------------------------------------------------- 1 | name: Lint TS/JS 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - "*" 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - name: Checkout Repository 14 | uses: actions/checkout@v3 15 | 16 | - name: Setup Node.js 17 | uses: actions/setup-node@v3 18 | with: 19 | node-version: "16" 20 | registry-url: "https://registry.npmjs.org" 21 | 22 | - name: Install Dependencies 23 | run: yarn install 24 | 25 | - name: Lint 26 | run: yarn lint 27 | -------------------------------------------------------------------------------- /packages/zeta-app-contracts/contracts/shared/ZetaEthMock.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.7; 3 | 4 | import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; 5 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 6 | import "@openzeppelin/contracts/utils/Context.sol"; 7 | 8 | contract ZetaEthMock is ERC20("Zeta", "ZETA") { 9 | constructor(address creator, uint256 initialSupply) { 10 | _mint(creator, initialSupply * (10 ** uint256(decimals()))); 11 | } 12 | 13 | function deposit() external payable { 14 | _mint(_msgSender(), msg.value); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/zevm-app-contracts/test/zeta-points/test.helpers.ts: -------------------------------------------------------------------------------- 1 | import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; 2 | import { ethers } from "hardhat"; 3 | 4 | export const getInvitationSig = async (signer: SignerWithAddress, expirationDate: number) => { 5 | const payload = ethers.utils.defaultAbiCoder.encode(["address", "uint256"], [signer.address, expirationDate]); 6 | 7 | const payloadHash = ethers.utils.keccak256(payload); 8 | 9 | // This adds the message prefix 10 | const signature = await signer.signMessage(ethers.utils.arrayify(payloadHash)); 11 | return ethers.utils.splitSignature(signature); 12 | }; 13 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "declaration": true, 5 | "esModuleInterop": true, 6 | "forceConsistentCasingInFileNames": true, 7 | "module": "commonjs", 8 | "moduleResolution": "node", 9 | "noImplicitAny": true, 10 | "outDir": "dist", 11 | "paths": { 12 | "@typechain-types": [ 13 | "typechain-types" 14 | ] 15 | }, 16 | "resolveJsonModule": true, 17 | "skipLibCheck": true, 18 | "sourceMap": false, 19 | "strict": true, 20 | "target": "es2020", 21 | "typeRoots": [ 22 | "@types", 23 | "./node_modules/@types" 24 | ] 25 | }, 26 | "exclude": [ 27 | ".yarn", 28 | "node_modules" 29 | ] 30 | } -------------------------------------------------------------------------------- /.github/workflows/test.yaml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - "*" 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - name: Checkout Repository 14 | uses: actions/checkout@v3 15 | 16 | - name: Setup Node.js 17 | uses: actions/setup-node@v3 18 | with: 19 | node-version: "16" 20 | registry-url: "https://registry.npmjs.org" 21 | 22 | - name: Install Dependencies 23 | run: yarn install 24 | 25 | - name: Test 26 | run: yarn test 27 | working-directory: packages/zeta-app-contracts 28 | 29 | - name: Test 30 | run: yarn test 31 | working-directory: packages/zevm-app-contracts 32 | -------------------------------------------------------------------------------- /packages/zevm-app-contracts/scripts/explorer.helpers.ts: -------------------------------------------------------------------------------- 1 | import { run } from "hardhat"; 2 | 3 | export const verifyContract = async (contractAddress: string, constructorArguments: any[], contract?: string) => { 4 | // Verification process 5 | console.log(`Verifying contract ${contractAddress}...`); 6 | try { 7 | if (contract) { 8 | await run("verify:verify", { 9 | address: contractAddress, 10 | constructorArguments, 11 | contract, 12 | }); 13 | } else { 14 | await run("verify:verify", { 15 | address: contractAddress, 16 | constructorArguments, 17 | }); 18 | } 19 | console.log("Verification successful"); 20 | } catch (error) { 21 | console.error("Verification failed:", error); 22 | } 23 | }; 24 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.eol": "\n", 3 | "typescript.preferences.importModuleSpecifier": "relative", 4 | "workbench.colorCustomizations": { 5 | "statusBar.background": "#4b8b54", 6 | "statusBarItem.hoverBackground": "#71C87D" 7 | }, 8 | "editor.codeActionsOnSave": { 9 | "source.fixAll.eslint": "explicit" 10 | }, 11 | "[solidity]": { 12 | "editor.tabSize": 4, 13 | "editor.formatOnSave": true, 14 | "editor.defaultFormatter": "NomicFoundation.hardhat-solidity" 15 | }, 16 | "[typescript]": { 17 | "editor.tabSize": 2, 18 | "editor.defaultFormatter": "dbaeumer.vscode-eslint" 19 | }, 20 | "[javascript]": { 21 | "editor.tabSize": 2, 22 | "editor.defaultFormatter": "dbaeumer.vscode-eslint" 23 | }, 24 | "solidity.defaultCompiler": "localNodeModule" 25 | } -------------------------------------------------------------------------------- /.github/workflows/gitguardian.yml: -------------------------------------------------------------------------------- 1 | name: GitGuardian scan 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | scanning: 7 | name: GitGuardian scan 8 | runs-on: ubuntu-latest 9 | steps: 10 | - name: Checkout 11 | uses: actions/checkout@v2 12 | with: 13 | fetch-depth: 0 # fetch all history so multiple commits can be scanned 14 | - name: GitGuardian scan 15 | uses: GitGuardian/ggshield-action@master 16 | env: 17 | GITHUB_PUSH_BEFORE_SHA: ${{ github.event.before }} 18 | GITHUB_PUSH_BASE_SHA: ${{ github.event.base }} 19 | GITHUB_PULL_BASE_SHA: ${{ github.event.pull_request.base.sha }} 20 | GITHUB_DEFAULT_BRANCH: ${{ github.event.repository.default_branch }} 21 | GITGUARDIAN_API_KEY: ${{ secrets.GITGUARDIAN_API_KEY }} -------------------------------------------------------------------------------- /packages/zevm-app-contracts/contracts/liquidity-incentives/Synthetixio/RewardsDistributionRecipient.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.7; 3 | 4 | // Inheritance 5 | import "./Owned.sol"; 6 | 7 | // https://docs.synthetix.io/contracts/source/contracts/rewardsdistributionrecipient 8 | abstract contract RewardsDistributionRecipient is Owned { 9 | address public rewardsDistribution; 10 | 11 | function notifyRewardAmount(uint256 reward) external virtual; 12 | 13 | modifier onlyRewardsDistribution() { 14 | require(msg.sender == rewardsDistribution, "Caller is not RewardsDistribution contract"); 15 | _; 16 | } 17 | 18 | function setRewardsDistribution(address _rewardsDistribution) external onlyOwner { 19 | rewardsDistribution = _rewardsDistribution; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /packages/zevm-app-contracts/scripts/zeta-swap/send.ts: -------------------------------------------------------------------------------- 1 | import { parseEther } from "@ethersproject/units"; 2 | import { getAddress, isProtocolNetworkName } from "@zetachain/protocol-contracts"; 3 | import { ethers } from "hardhat"; 4 | import { network } from "hardhat"; 5 | 6 | const main = async () => { 7 | if (!isProtocolNetworkName(network.name)) throw new Error("Invalid network name"); 8 | console.log(`Sending native token...`); 9 | 10 | const [signer] = await ethers.getSigners(); 11 | 12 | const tssAddress = getAddress("tss", network.name); 13 | 14 | const tx = await signer.sendTransaction({ 15 | to: tssAddress, 16 | value: parseEther("30"), 17 | }); 18 | 19 | console.log("Token sent. tx:", tx.hash); 20 | }; 21 | 22 | main().catch((error) => { 23 | console.error(error); 24 | process.exit(1); 25 | }); 26 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | 2 | FROM node:16.14-alpine 3 | 4 | ENV SHELL /bin/ash 5 | ENV EXECUTE_PROGRAMMATICALLY=true 6 | 7 | RUN apk add --update python3 yarn git nodejs make g++ 8 | 9 | ## Install node modules before the code is copied to the container 10 | WORKDIR /home/zetachain/ 11 | COPY package*.json ./ 12 | COPY packages/zeta-app-contracts/package.json ./packages/zeta-app-contracts/package.json 13 | COPY packages/zevm-app-contracts/package.json ./packages/zevm-app-contracts/package.json 14 | RUN yarn install ; exit 0 15 | 16 | COPY . ./ 17 | RUN yarn install 18 | 19 | RUN yarn add solc@0.5.10 solc@0.6.6 solc@0.7.6 solc@0.8.7 20 | 21 | RUN cd packages/zeta-app-contracts && npx hardhat compile && cd - 22 | RUN cd packages/zevm-app-contracts && npx hardhat compile && cd - 23 | 24 | WORKDIR /home/zetachain/ 25 | 26 | ENTRYPOINT ["ash"] 27 | 28 | -------------------------------------------------------------------------------- /packages/zevm-app-contracts/contracts/shared/BytesHelperLib.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity =0.8.7; 3 | 4 | library BytesHelperLib { 5 | function bytesToAddress(bytes calldata data, uint256 offset) internal pure returns (address output) { 6 | bytes memory b = data[offset:offset + 20]; 7 | assembly { 8 | output := mload(add(b, 20)) 9 | } 10 | } 11 | 12 | function bytesToUint32(bytes calldata data, uint256 offset) internal pure returns (uint32 output) { 13 | bytes memory b = data[offset:offset + 4]; 14 | assembly { 15 | output := mload(add(b, 4)) 16 | } 17 | } 18 | 19 | function addressToBytes(address someAddress) internal pure returns (bytes32) { 20 | return bytes32(uint256(uint160(someAddress))); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /packages/zevm-app-contracts/scripts/disperse/deploy.ts: -------------------------------------------------------------------------------- 1 | import { isProtocolNetworkName } from "@zetachain/protocol-contracts"; 2 | import { ethers, network } from "hardhat"; 3 | 4 | import { Disperse__factory } from "../../typechain-types"; 5 | import { saveAddress } from "../address.helpers"; 6 | 7 | const networkName = network.name; 8 | 9 | async function main() { 10 | if (!isProtocolNetworkName(networkName)) throw new Error("Invalid network name"); 11 | 12 | const DisperseFactory = (await ethers.getContractFactory("Disperse")) as Disperse__factory; 13 | 14 | const disperseFactory = await DisperseFactory.deploy(); 15 | await disperseFactory.deployed(); 16 | console.log("Disperse deployed to:", disperseFactory.address); 17 | saveAddress("disperse", disperseFactory.address, networkName); 18 | } 19 | 20 | main().catch((error) => { 21 | console.error(error); 22 | process.exit(1); 23 | }); 24 | -------------------------------------------------------------------------------- /packages/zeta-app-contracts/scripts/multi-chain-value/verify-contract.ts: -------------------------------------------------------------------------------- 1 | import { getAddress, isProtocolNetworkName } from "@zetachain/protocol-contracts"; 2 | import hardhat from "hardhat"; 3 | import { network } from "hardhat"; 4 | 5 | import { getAppAddress } from "../address.helpers"; 6 | 7 | const networkName = network.name; 8 | 9 | async function main() { 10 | if (!isProtocolNetworkName(networkName)) throw new Error("Invalid network name"); 11 | 12 | await hardhat.run("verify:verify", { 13 | address: getAppAddress("multiChainValue", networkName), 14 | constructorArguments: [getAddress("connector", networkName), getAddress("zetaToken", networkName)], 15 | contract: "contracts/multi-chain-value/MultiChainValue.sol:MultiChainValue", 16 | }); 17 | } 18 | 19 | main() 20 | .then(() => process.exit(0)) 21 | .catch((error) => { 22 | console.error(error); 23 | process.exit(1); 24 | }); 25 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to ZetaChain 2 | 3 | First, thank you! We really appreciate that you're considering contributing to ZetaChain. 4 | 5 | ## Ways to contribute 6 | 7 | There are many ways to contribute to ZetaChain, writing code is one of them. Others are improving our docs, or being part of the network by running a node. 8 | 9 | ## Your first contribution 10 | 11 | First time contributing to Open Source? No worries, you can learn how to approach it from [this free video series](https://egghead.io/courses/how-to-contribute-to-an-open-source-project-on-github) or from [this guide](https://opensource.guide/how-to-contribute/) if you prefer reading. 12 | 13 | ## Opening a Pull Request 14 | 15 | A few rules of thumb based on the size of your Pull Request: 16 | 17 | * If it's small, like fixing a typo, just send it. 18 | * If it's medium, like working on a feature or a bug, you may want to open an issue to chat with the maintainers before working on it. 19 | * If it's big, do your best to split it into smaller ones. 20 | -------------------------------------------------------------------------------- /packages/zevm-app-contracts/scripts/withdrawERC20/deploy.ts: -------------------------------------------------------------------------------- 1 | import { isProtocolNetworkName } from "@zetachain/protocol-contracts"; 2 | import { ethers, network } from "hardhat"; 3 | 4 | import { WithdrawERC20__factory } from "../../typechain-types/factories/contracts/withdrawErc20/withdrawErc20.sol"; 5 | import { getSystemContractAddress, saveAddress } from "../address.helpers"; 6 | 7 | const networkName = network.name; 8 | 9 | const SYSTEM_CONTRACT = getSystemContractAddress(); 10 | 11 | async function main() { 12 | if (!isProtocolNetworkName(networkName)) throw new Error("Invalid network name"); 13 | 14 | const WithdrawERC20Factory = (await ethers.getContractFactory("WithdrawERC20")) as WithdrawERC20__factory; 15 | 16 | const withdrawERC20 = await WithdrawERC20Factory.deploy(SYSTEM_CONTRACT); 17 | await withdrawERC20.deployed(); 18 | 19 | console.log("WithdrawERC20 deployed to:", withdrawERC20.address); 20 | saveAddress("withdrawERC20", withdrawERC20.address, networkName); 21 | } 22 | 23 | main().catch((error) => { 24 | console.error(error); 25 | process.exit(1); 26 | }); 27 | -------------------------------------------------------------------------------- /packages/zevm-app-contracts/test/zeta-points/invitationManager.helpers.ts: -------------------------------------------------------------------------------- 1 | import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; 2 | 3 | export interface Enrollment { 4 | to: string; 5 | } 6 | 7 | export interface EnrollmentSigned extends Enrollment { 8 | signature: string; 9 | signatureExpiration: number; 10 | } 11 | 12 | export const getEnrollmentSignature = async ( 13 | chainId: number, 14 | verifyingContract: string, 15 | signer: SignerWithAddress, 16 | signatureExpiration: number, 17 | to: string 18 | ) => { 19 | const domain = { 20 | chainId: chainId, 21 | name: "InvitationManagerV2", 22 | verifyingContract: verifyingContract, 23 | version: "1", 24 | }; 25 | 26 | const types = { 27 | Verify: [ 28 | { name: "to", type: "address" }, 29 | { name: "signatureExpiration", type: "uint256" }, 30 | ], 31 | }; 32 | 33 | const value = { 34 | signatureExpiration, 35 | to, 36 | }; 37 | // Signing the data 38 | const signature = await signer._signTypedData(domain, types, value); 39 | return signature; 40 | }; 41 | -------------------------------------------------------------------------------- /packages/zevm-app-contracts/contracts/liquidity-incentives/Synthetixio/interfaces/IStakingRewards.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.7; 3 | 4 | // https://docs.synthetix.io/contracts/source/interfaces/istakingrewards 5 | interface IStakingRewards { 6 | // Views 7 | 8 | function balanceOf(address account) external view returns (uint256); 9 | 10 | function earned(address account) external view returns (uint256); 11 | 12 | function getRewardForDuration() external view returns (uint256); 13 | 14 | function lastTimeRewardApplicable() external view returns (uint256); 15 | 16 | function rewardPerToken() external view returns (uint256); 17 | 18 | function rewardsDistribution() external view returns (address); 19 | 20 | function rewardsToken() external view returns (address); 21 | 22 | function totalSupply() external view returns (uint256); 23 | 24 | // Mutative 25 | 26 | function exit() external; 27 | 28 | function getReward() external; 29 | 30 | function stake(uint256 amount) external; 31 | 32 | function withdraw(uint256 amount) external; 33 | } 34 | -------------------------------------------------------------------------------- /packages/zevm-app-contracts/contracts/withdrawErc20/withdrawErc20.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.7; 3 | 4 | import "@zetachain/protocol-contracts/contracts/zevm/SystemContract.sol"; 5 | 6 | import "@zetachain/toolkit/contracts/SwapHelperLib.sol"; 7 | 8 | contract WithdrawERC20 { 9 | SystemContract public immutable systemContract; 10 | 11 | error InsufficientInputAmount(); 12 | 13 | constructor(address systemContractAddress) { 14 | systemContract = SystemContract(systemContractAddress); 15 | } 16 | 17 | function withdraw(address zrc20, uint256 amount, bytes memory to) external virtual { 18 | IZRC20(zrc20).transferFrom(msg.sender, address(this), amount); 19 | 20 | (address gasZRC20, uint256 gasFee) = IZRC20(zrc20).withdrawGasFee(); 21 | 22 | uint256 inputForGas = SwapHelperLib.swapTokensForExactTokens(systemContract, zrc20, gasFee, gasZRC20, amount); 23 | 24 | if (inputForGas > amount) revert InsufficientInputAmount(); 25 | 26 | IZRC20(gasZRC20).approve(zrc20, gasFee); 27 | IZRC20(zrc20).withdraw(to, amount - inputForGas); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /packages/zevm-app-contracts/scripts/zeta-points/deploy.ts: -------------------------------------------------------------------------------- 1 | import { isProtocolNetworkName } from "@zetachain/protocol-contracts"; 2 | import { ethers, network } from "hardhat"; 3 | 4 | import { InvitationManager__factory } from "../../typechain-types"; 5 | import { saveAddress } from "../address.helpers"; 6 | 7 | const networkName = network.name; 8 | 9 | const invitationManager = async () => { 10 | if (!isProtocolNetworkName(networkName)) throw new Error("Invalid network name"); 11 | 12 | const InvitationManagerFactory = (await ethers.getContractFactory("InvitationManager")) as InvitationManager__factory; 13 | const invitationManager = await InvitationManagerFactory.deploy(); 14 | await invitationManager.deployed(); 15 | console.log("InvitationManager deployed to:", invitationManager.address); 16 | saveAddress("invitationManager", invitationManager.address, networkName); 17 | }; 18 | 19 | const main = async () => { 20 | if (!isProtocolNetworkName(networkName)) throw new Error("Invalid network name"); 21 | await invitationManager(); 22 | }; 23 | 24 | main().catch((error) => { 25 | console.error(error); 26 | process.exit(1); 27 | }); 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Meta Protocol, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/zevm-app-contracts/scripts/proof-of-liveness/deploy.ts: -------------------------------------------------------------------------------- 1 | import { isProtocolNetworkName } from "@zetachain/protocol-contracts"; 2 | import { ethers, network } from "hardhat"; 3 | 4 | import { ProofOfLiveness__factory } from "../../typechain-types"; 5 | import { saveAddress } from "../address.helpers"; 6 | import { verifyContract } from "../explorer.helpers"; 7 | 8 | const networkName = network.name; 9 | 10 | const deployProofOfLiveness = async () => { 11 | if (!isProtocolNetworkName(networkName)) throw new Error("Invalid network name"); 12 | 13 | const ProofOfLivenessFactory = (await ethers.getContractFactory("ProofOfLiveness")) as ProofOfLiveness__factory; 14 | const ProofOfLiveness = await ProofOfLivenessFactory.deploy(); 15 | 16 | await ProofOfLiveness.deployed(); 17 | 18 | console.log("ProofOfLiveness deployed to:", ProofOfLiveness.address); 19 | 20 | saveAddress("ProofOfLiveness", ProofOfLiveness.address, networkName); 21 | 22 | await verifyContract(ProofOfLiveness.address, []); 23 | }; 24 | 25 | const main = async () => { 26 | if (!isProtocolNetworkName(networkName)) throw new Error("Invalid network name"); 27 | await deployProofOfLiveness(); 28 | }; 29 | 30 | main().catch((error) => { 31 | console.error(error); 32 | process.exit(1); 33 | }); 34 | -------------------------------------------------------------------------------- /packages/zevm-app-contracts/test/instant-rewards/test.helpers.ts: -------------------------------------------------------------------------------- 1 | import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; 2 | import { BigNumber } from "ethers"; 3 | 4 | export interface ClaimData { 5 | amount: BigNumber; 6 | sigExpiration: number; 7 | taskId: string; 8 | to: string; 9 | } 10 | 11 | export interface ClaimDataSigned extends ClaimData { 12 | signature: string; 13 | } 14 | 15 | export const getSignature = async ( 16 | chainId: number, 17 | verifyingContract: string, 18 | signer: SignerWithAddress, 19 | claimData: ClaimData 20 | ) => { 21 | const domain = { 22 | chainId: chainId, 23 | name: "InstantRewards", 24 | verifyingContract: verifyingContract, 25 | version: "1", 26 | }; 27 | 28 | const types = { 29 | Claim: [ 30 | { name: "to", type: "address" }, 31 | { name: "sigExpiration", type: "uint256" }, 32 | { name: "taskId", type: "bytes32" }, 33 | { name: "amount", type: "uint256" }, 34 | ], 35 | }; 36 | 37 | const value = { 38 | amount: claimData.amount, 39 | sigExpiration: claimData.sigExpiration, 40 | taskId: claimData.taskId, 41 | to: claimData.to, 42 | }; 43 | // Signing the data 44 | const signature = await signer._signTypedData(domain, types, value); 45 | return signature; 46 | }; 47 | -------------------------------------------------------------------------------- /packages/zevm-app-contracts/scripts/xp-nft/upgrade-v2.ts: -------------------------------------------------------------------------------- 1 | import { isProtocolNetworkName } from "@zetachain/protocol-contracts"; 2 | import { ethers, network, upgrades } from "hardhat"; 3 | 4 | import addresses from "../../data/addresses.json"; 5 | import { saveAddress } from "../address.helpers"; 6 | import { verifyContract } from "../explorer.helpers"; 7 | 8 | const networkName = network.name; 9 | 10 | const upgradeZetaXP = async () => { 11 | if (!isProtocolNetworkName(networkName)) throw new Error("Invalid network name"); 12 | 13 | //@ts-ignore 14 | const nftAddress = addresses["zevm"][networkName].ZetaXP; 15 | 16 | const ZetaXPFactory = await ethers.getContractFactory("ZetaXP_V2"); 17 | const zetaXP = await upgrades.upgradeProxy(nftAddress, ZetaXPFactory); 18 | const implementationAddress = await upgrades.erc1967.getImplementationAddress(zetaXP.address); 19 | 20 | console.log("ZetaXP upgraded in:", zetaXP.address); 21 | console.log("ZetaXP implementation deployed to:", implementationAddress); 22 | 23 | saveAddress("ZetaXP", zetaXP.address, networkName); 24 | 25 | await verifyContract(implementationAddress, []); 26 | }; 27 | 28 | const main = async () => { 29 | await upgradeZetaXP(); 30 | }; 31 | 32 | main().catch((error) => { 33 | console.error(error); 34 | process.exit(1); 35 | }); 36 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | 3 | const OFF = 0; 4 | 5 | /** 6 | * @type {import("eslint").Linter.Config} 7 | */ 8 | module.exports = { 9 | env: { 10 | browser: false, 11 | es2021: true, 12 | mocha: true, 13 | node: true, 14 | }, 15 | extends: ["plugin:prettier/recommended"], 16 | parser: "@typescript-eslint/parser", 17 | parserOptions: { 18 | ecmaVersion: 12, 19 | }, 20 | plugins: ["@typescript-eslint", "prettier", "simple-import-sort", "sort-keys-fix", "typescript-sort-keys"], 21 | rules: { 22 | "@typescript-eslint/sort-type-union-intersection-members": "error", 23 | camelcase: "off", 24 | "no-console": OFF, 25 | "simple-import-sort/exports": "error", 26 | "simple-import-sort/imports": "error", 27 | "sort-keys-fix/sort-keys-fix": "error", 28 | "typescript-sort-keys/interface": "error", 29 | "typescript-sort-keys/string-enum": "error", 30 | }, 31 | settings: { 32 | "import/parsers": { 33 | "@typescript-eslint/parser": [".js", ".jsx", ".ts", ".tsx", ".d.ts"], 34 | }, 35 | "import/resolver": { 36 | node: { 37 | extensions: [".js", ".jsx", ".ts", ".tsx", ".d.ts"], 38 | }, 39 | typescript: { 40 | project: path.join(__dirname, "tsconfig.json"), 41 | }, 42 | }, 43 | }, 44 | }; 45 | -------------------------------------------------------------------------------- /packages/zevm-app-contracts/scripts/zeta-swap/swap.ts: -------------------------------------------------------------------------------- 1 | import { BigNumber } from "@ethersproject/bignumber"; 2 | import { parseEther } from "@ethersproject/units"; 3 | import { getAddress, getZRC20Address, isProtocolNetworkName } from "@zetachain/protocol-contracts"; 4 | import { ethers, network } from "hardhat"; 5 | 6 | import { getZEVMAppAddress } from "../address.helpers"; 7 | import { getSwapData } from "./helpers"; 8 | 9 | const main = async () => { 10 | if (!isProtocolNetworkName(network.name)) throw new Error("Invalid network name"); 11 | 12 | const destinationToken = 13 | network.name == "goerli_testnet" ? getZRC20Address("mumbai_testnet") : getZRC20Address("goerli_testnet"); 14 | 15 | console.log(`Swapping native token...`); 16 | 17 | const [signer] = await ethers.getSigners(); 18 | 19 | const zetaSwapAddress = getZEVMAppAddress("zetaSwap"); 20 | 21 | const tssAddress = getAddress("tss", network.name); 22 | 23 | const data = getSwapData(zetaSwapAddress, signer.address, destinationToken, BigNumber.from("0")); 24 | 25 | const tx = await signer.sendTransaction({ 26 | data, 27 | to: tssAddress, 28 | value: parseEther("0.005"), 29 | }); 30 | 31 | console.log("tx:", tx.hash); 32 | }; 33 | 34 | main().catch((error) => { 35 | console.error(error); 36 | process.exit(1); 37 | }); 38 | -------------------------------------------------------------------------------- /packages/zevm-app-contracts/contracts/liquidity-incentives/Synthetixio/Owned.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.7; 3 | 4 | // https://docs.synthetix.io/contracts/source/contracts/owned 5 | contract Owned { 6 | address public owner; 7 | address public nominatedOwner; 8 | 9 | constructor(address _owner) public { 10 | require(_owner != address(0), "Owner address cannot be 0"); 11 | owner = _owner; 12 | emit OwnerChanged(address(0), _owner); 13 | } 14 | 15 | function nominateNewOwner(address _owner) external onlyOwner { 16 | nominatedOwner = _owner; 17 | emit OwnerNominated(_owner); 18 | } 19 | 20 | function acceptOwnership() external { 21 | require(msg.sender == nominatedOwner, "You must be nominated before you can accept ownership"); 22 | emit OwnerChanged(owner, nominatedOwner); 23 | owner = nominatedOwner; 24 | nominatedOwner = address(0); 25 | } 26 | 27 | modifier onlyOwner() { 28 | _onlyOwner(); 29 | _; 30 | } 31 | 32 | function _onlyOwner() private view { 33 | require(msg.sender == owner, "Only the contract owner may perform this action"); 34 | } 35 | 36 | event OwnerNominated(address newOwner); 37 | event OwnerChanged(address oldOwner, address newOwner); 38 | } 39 | -------------------------------------------------------------------------------- /packages/zevm-app-contracts/scripts/liquidity-incentives/add-reward.ts: -------------------------------------------------------------------------------- 1 | import { BigNumber } from "@ethersproject/bignumber"; 2 | import { isProtocolNetworkName } from "@zetachain/protocol-contracts"; 3 | import { parseEther } from "ethers/lib/utils"; 4 | import { ethers, network } from "hardhat"; 5 | 6 | import { SystemContract__factory } from "../../typechain-types"; 7 | import { getSystemContractAddress } from "../address.helpers"; 8 | import { addReward } from "./helpers"; 9 | 10 | const REWARD_DURATION = BigNumber.from("604800").mul(8); // 1 week * 8 11 | const REWARDS_AMOUNT = parseEther("500"); 12 | const REWARD_CONTRACT_ADDRESS = "0x0dee8b6e2d2035a798b67c68d47f941718a62263"; //@dev: change this to the address of the reward contract 13 | 14 | const main = async () => { 15 | const [deployer] = await ethers.getSigners(); 16 | const networkName = network.name; 17 | 18 | if (!isProtocolNetworkName(networkName)) throw new Error("Invalid network name"); 19 | 20 | const systemContractAddress = getSystemContractAddress(); 21 | const systemContract = await SystemContract__factory.connect(systemContractAddress, deployer); 22 | 23 | await addReward(deployer, systemContract, REWARD_CONTRACT_ADDRESS, REWARD_DURATION, REWARDS_AMOUNT); 24 | }; 25 | 26 | main().catch((error) => { 27 | console.error(error); 28 | process.exit(1); 29 | }); 30 | -------------------------------------------------------------------------------- /packages/zeta-app-contracts/scripts/address.helpers.ts: -------------------------------------------------------------------------------- 1 | import { networks } from "@zetachain/networks"; 2 | import { isProtocolNetworkName, ZetaProtocolNetwork } from "@zetachain/protocol-contracts"; 3 | import { readFileSync, writeFileSync } from "fs"; 4 | import { network } from "hardhat"; 5 | import { join } from "path"; 6 | 7 | import addresses from "../data/addresses.json"; 8 | 9 | export type AppAddress = "multiChainSwap" | "multiChainValue" | "zetaTokenConsumerUniV2" | "zetaTokenConsumerUniV3"; 10 | 11 | export const getAppAddress = (address: AppAddress, network: ZetaProtocolNetwork): string => { 12 | return (addresses["ccm"] as any)[network][address]; 13 | }; 14 | 15 | export const getChainId = (network: ZetaProtocolNetwork): number => { 16 | //@ts-ignore 17 | return networks[network].chain_id; 18 | }; 19 | 20 | export const saveAddress = (name: string, address: string) => { 21 | const networkName = network.name; 22 | if (!isProtocolNetworkName(networkName)) throw new Error("Invalid network name"); 23 | 24 | console.log(`Updating ${name} address on ${networkName}.`); 25 | 26 | const filename = join(__dirname, `../data/addresses.json`); 27 | 28 | const newAddresses = JSON.parse(readFileSync(filename, "utf8")); 29 | 30 | newAddresses["ccm"][networkName][name] = address; 31 | 32 | writeFileSync(filename, JSON.stringify(newAddresses, null, 2)); 33 | }; 34 | -------------------------------------------------------------------------------- /packages/zevm-app-contracts/scripts/xp-nft/gov-query-proposals.ts: -------------------------------------------------------------------------------- 1 | import { isProtocolNetworkName } from "@zetachain/protocol-contracts"; 2 | import { ethers, network } from "hardhat"; 3 | 4 | import { getZEVMAppAddress } from "../address.helpers"; 5 | 6 | const networkName = network.name; 7 | 8 | const callGov = async () => { 9 | const govAddress = getZEVMAppAddress("ZetaXPGov", networkName); 10 | console.log("ZetaXPGov address:", govAddress); 11 | 12 | const ZetaXPGovFactory = await ethers.getContractFactory("ZetaXPGov"); 13 | const zetaGov = await ZetaXPGovFactory.attach(govAddress); 14 | 15 | const start = 7426910 - 1; 16 | const end = 7441292 + 1; 17 | for (let i = start; i < end; i += 1000) { 18 | const proposalIds = await zetaGov.queryFilter(zetaGov.filters.ProposalCreated(), i, i + 1000); 19 | if (proposalIds.length > 0) { 20 | for (let j = 0; j < proposalIds.length; j++) { 21 | const proposalId = proposalIds[j].proposalId; 22 | const votes = await zetaGov.proposalVotes(proposalId); 23 | console.log("Proposal ID:", proposalId); 24 | console.log("Votes:", votes); 25 | } 26 | } 27 | } 28 | }; 29 | 30 | const main = async () => { 31 | if (!isProtocolNetworkName(networkName)) throw new Error("Invalid network name"); 32 | await callGov(); 33 | }; 34 | 35 | main().catch((error) => { 36 | console.error(error); 37 | process.exit(1); 38 | }); 39 | -------------------------------------------------------------------------------- /packages/zevm-app-contracts/scripts/instant-rewards/deploy.ts: -------------------------------------------------------------------------------- 1 | import { isProtocolNetworkName } from "@zetachain/protocol-contracts"; 2 | import { ethers, network } from "hardhat"; 3 | 4 | import { InstantRewards__factory } from "../../typechain-types"; 5 | import { saveAddress } from "../address.helpers"; 6 | import { verifyContract } from "../explorer.helpers"; 7 | 8 | const networkName = network.name; 9 | 10 | const signer = "0x1d24d94520B94B26351f6573de5ef9731c48531A"; 11 | const owner = "0x1d24d94520B94B26351f6573de5ef9731c48531A"; 12 | 13 | const deployInstantRewards = async () => { 14 | if (!isProtocolNetworkName(networkName)) throw new Error("Invalid network name"); 15 | 16 | const InstantRewardsFactory = (await ethers.getContractFactory("InstantRewards")) as InstantRewards__factory; 17 | const InstantRewards = await InstantRewardsFactory.deploy(signer, owner); 18 | 19 | await InstantRewards.deployed(); 20 | 21 | console.log("InstantRewards deployed to:", InstantRewards.address); 22 | 23 | saveAddress("InstantRewards", InstantRewards.address, networkName); 24 | 25 | await verifyContract(InstantRewards.address, [signer, owner]); 26 | }; 27 | 28 | const main = async () => { 29 | if (!isProtocolNetworkName(networkName)) throw new Error("Invalid network name"); 30 | await deployInstantRewards(); 31 | }; 32 | 33 | main().catch((error) => { 34 | console.error(error); 35 | process.exit(1); 36 | }); 37 | -------------------------------------------------------------------------------- /packages/zevm-app-contracts/scripts/liquidity-incentives/deploy.ts: -------------------------------------------------------------------------------- 1 | import { isProtocolNetworkName } from "@zetachain/protocol-contracts"; 2 | import { ethers, network } from "hardhat"; 3 | 4 | import { RewardDistributorFactory__factory, SystemContract__factory } from "../../typechain-types"; 5 | import { getSystemContractAddress, saveAddress } from "../address.helpers"; 6 | 7 | const networkName = network.name; 8 | 9 | const SYSTEM_CONTRACT = getSystemContractAddress(); 10 | 11 | async function main() { 12 | const [deployer] = await ethers.getSigners(); 13 | if (!isProtocolNetworkName(networkName)) throw new Error("Invalid network name"); 14 | const systemContract = await SystemContract__factory.connect(SYSTEM_CONTRACT, deployer); 15 | const zetaTokenAddress = await systemContract.wZetaContractAddress(); 16 | 17 | const RewardDistributorFactoryFactory = (await ethers.getContractFactory( 18 | "RewardDistributorFactory" 19 | )) as RewardDistributorFactory__factory; 20 | 21 | const rewardDistributorFactory = await RewardDistributorFactoryFactory.deploy(zetaTokenAddress, SYSTEM_CONTRACT); 22 | await rewardDistributorFactory.deployed(); 23 | console.log("RewardDistributorFactory deployed to:", rewardDistributorFactory.address); 24 | saveAddress("rewardDistributorFactory", rewardDistributorFactory.address, networkName); 25 | } 26 | 27 | main().catch((error) => { 28 | console.error(error); 29 | process.exit(1); 30 | }); 31 | -------------------------------------------------------------------------------- /packages/zevm-app-contracts/scripts/zeta-points/deploy-invitationV2.ts: -------------------------------------------------------------------------------- 1 | import { isProtocolNetworkName } from "@zetachain/protocol-contracts"; 2 | import { ethers, network } from "hardhat"; 3 | 4 | import { InvitationManagerV2__factory } from "../../typechain-types"; 5 | import { getZEVMAppAddress, saveAddress } from "../address.helpers"; 6 | import { verifyContract } from "../explorer.helpers"; 7 | 8 | const networkName = network.name; 9 | 10 | const invitationManager = async () => { 11 | if (!isProtocolNetworkName(networkName)) throw new Error("Invalid network name"); 12 | 13 | const invitationManagerV1 = getZEVMAppAddress("invitationManager", networkName); 14 | 15 | const InvitationManagerFactory = (await ethers.getContractFactory( 16 | "InvitationManagerV2" 17 | )) as InvitationManagerV2__factory; 18 | const invitationManager = await InvitationManagerFactory.deploy(invitationManagerV1); 19 | await invitationManager.deployed(); 20 | console.log("InvitationManagerV2 deployed to:", invitationManager.address); 21 | saveAddress("invitationManagerV2", invitationManager.address, networkName); 22 | await verifyContract(invitationManager.address, [invitationManagerV1]); 23 | }; 24 | 25 | const main = async () => { 26 | if (!isProtocolNetworkName(networkName)) throw new Error("Invalid network name"); 27 | await invitationManager(); 28 | }; 29 | 30 | main().catch((error) => { 31 | console.error(error); 32 | process.exit(1); 33 | }); 34 | -------------------------------------------------------------------------------- /packages/zevm-app-contracts/contracts/xp-nft/xpNFT_V3.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.20; 3 | 4 | import "./xpNFT_V2.sol"; 5 | 6 | contract ZetaXP_V3 is ZetaXP_V2 { 7 | event TagUpdated(address indexed sender, uint256 indexed tokenId, bytes32 tag); 8 | 9 | function version() public pure override returns (string memory) { 10 | return "3.0.0"; 11 | } 12 | 13 | function _transfer(address from, address to, uint256 tokenId) internal override { 14 | bytes32 tag = tagByTokenId[tokenId]; 15 | if (tag != 0) { 16 | tokenByUserTag[from][tag] = 0; 17 | } 18 | if (tokenByUserTag[to][tag] == 0) { 19 | tokenByUserTag[to][tag] = tokenId; 20 | } 21 | ERC721Upgradeable._transfer(from, to, tokenId); 22 | } 23 | 24 | function moveTagToToken(uint256 tokenId, bytes32 tag) external { 25 | uint256 currentTokenId = tokenByUserTag[msg.sender][tag]; 26 | address owner = ownerOf(tokenId); 27 | if (owner != msg.sender) { 28 | revert TransferNotAllowed(); 29 | } 30 | if (currentTokenId == tokenId) { 31 | return; 32 | } 33 | if (currentTokenId != 0) { 34 | tagByTokenId[currentTokenId] = 0; 35 | } 36 | 37 | tagByTokenId[tokenId] = tag; 38 | tokenByUserTag[msg.sender][tag] = tokenId; 39 | emit TagUpdated(msg.sender, tokenId, tag); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /packages/zevm-app-contracts/scripts/address.helpers.ts: -------------------------------------------------------------------------------- 1 | import { networks } from "@zetachain/networks"; 2 | import { ZetaProtocolNetwork } from "@zetachain/protocol-contracts"; 3 | import protocolAddresses from "@zetachain/protocol-contracts/dist/data/addresses.json"; 4 | import { readFileSync, writeFileSync } from "fs"; 5 | import { join } from "path"; 6 | 7 | import addresses from "../data/addresses.json"; 8 | 9 | export const getZEVMAppAddress = (address: string, network: string = "zeta_testnet"): string => { 10 | return (addresses["zevm"] as any)[network][address]; 11 | }; 12 | 13 | export const getChainId = (network: ZetaProtocolNetwork): number => { 14 | //@ts-ignore 15 | return networks[network].chain_id; 16 | }; 17 | 18 | export const getGasSymbolByNetwork = (network: ZetaProtocolNetwork): number => { 19 | //@ts-ignore 20 | return networks[network].gas_symbol; 21 | }; 22 | 23 | export const getSystemContractAddress = () => { 24 | return protocolAddresses["zevm"]["zeta_testnet"].systemContract; 25 | }; 26 | 27 | export const saveAddress = (name: string, address: string, networkName: ZetaProtocolNetwork) => { 28 | console.log(`Updating ${name} address on ${networkName}.`); 29 | 30 | const filename = join(__dirname, `../data/addresses.json`); 31 | 32 | const newAddresses = JSON.parse(readFileSync(filename, "utf8")); 33 | 34 | newAddresses["zevm"][networkName][name] = address; 35 | 36 | writeFileSync(filename, JSON.stringify(newAddresses, null, 2)); 37 | }; 38 | -------------------------------------------------------------------------------- /packages/zevm-app-contracts/scripts/withdrawERC20/withdraw.ts: -------------------------------------------------------------------------------- 1 | import { isProtocolNetworkName } from "@zetachain/protocol-contracts"; 2 | import { ethers, network } from "hardhat"; 3 | 4 | import { ERC20__factory } from "../../typechain-types"; 5 | import { WithdrawERC20__factory } from "../../typechain-types/factories/contracts/withdrawErc20/withdrawErc20.sol"; 6 | import { getZEVMAppAddress } from "../address.helpers"; 7 | 8 | const networkName = network.name; 9 | 10 | const ZUSDC_ADDRESS = "0x0cbe0dF132a6c6B4a2974Fa1b7Fb953CF0Cc798a"; 11 | const AMOUNT = ethers.utils.parseUnits("0.5", 6); 12 | 13 | async function main() { 14 | if (!isProtocolNetworkName(networkName)) throw new Error("Invalid network name"); 15 | 16 | const [signer] = await ethers.getSigners(); 17 | const withdrawERC20Address = getZEVMAppAddress("withdrawERC20"); 18 | 19 | const WithdrawERC20Factory = (await ethers.getContractFactory("WithdrawERC20")) as WithdrawERC20__factory; 20 | const WithdrawERC20 = WithdrawERC20Factory.attach(withdrawERC20Address); 21 | 22 | const ERC20Factory = (await ethers.getContractFactory("ERC20")) as ERC20__factory; 23 | const erc20 = ERC20Factory.attach(ZUSDC_ADDRESS); 24 | 25 | await erc20.approve(WithdrawERC20.address, AMOUNT); 26 | const tx = await WithdrawERC20.withdraw(erc20.address, AMOUNT, signer.address); 27 | console.log(`Sending transaction ${tx.hash}`); 28 | } 29 | 30 | main().catch((error) => { 31 | console.error(error); 32 | process.exit(1); 33 | }); 34 | -------------------------------------------------------------------------------- /packages/zevm-app-contracts/scripts/zeta-swap/deploy.ts: -------------------------------------------------------------------------------- 1 | import { isProtocolNetworkName } from "@zetachain/protocol-contracts"; 2 | import { ethers, network } from "hardhat"; 3 | 4 | import { ZetaSwap, ZetaSwap__factory, ZetaSwapBtcInbound, ZetaSwapBtcInbound__factory } from "../../typechain-types"; 5 | import { getSystemContractAddress, saveAddress } from "../address.helpers"; 6 | 7 | const networkName = network.name; 8 | 9 | const main = async () => { 10 | if (!isProtocolNetworkName(networkName)) throw new Error("Invalid network name"); 11 | 12 | console.log(`Deploying ZetaSwap...`); 13 | const SYSTEM_CONTRACT = getSystemContractAddress(); 14 | 15 | const Factory = (await ethers.getContractFactory("ZetaSwap")) as ZetaSwap__factory; 16 | const contract = (await Factory.deploy(SYSTEM_CONTRACT)) as ZetaSwap; 17 | await contract.deployed(); 18 | 19 | console.log("Deployed ZetaSwap. Address:", contract.address); 20 | saveAddress("zetaSwap", contract.address, networkName); 21 | 22 | const FactoryBTC = (await ethers.getContractFactory("ZetaSwapBtcInbound")) as ZetaSwapBtcInbound__factory; 23 | const contractBTC = (await FactoryBTC.deploy(SYSTEM_CONTRACT)) as ZetaSwapBtcInbound; 24 | await contractBTC.deployed(); 25 | 26 | console.log("Deployed zetaSwapBtcInbound. Address:", contractBTC.address); 27 | saveAddress("zetaSwapBtcInbound", contractBTC.address, networkName); 28 | }; 29 | 30 | main().catch((error) => { 31 | console.error(error); 32 | process.exit(1); 33 | }); 34 | -------------------------------------------------------------------------------- /packages/zevm-app-contracts/contracts/liquidity-incentives/Synthetixio/Pausable.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.7; 3 | 4 | // Inheritance 5 | import "./Owned.sol"; 6 | 7 | // https://docs.synthetix.io/contracts/source/contracts/pausable 8 | abstract contract Pausable is Owned { 9 | uint public lastPauseTime; 10 | bool public paused; 11 | 12 | constructor() { 13 | // This contract is abstract, and thus cannot be instantiated directly 14 | require(owner != address(0), "Owner must be set"); 15 | // Paused will be false, and lastPauseTime will be 0 upon initialisation 16 | } 17 | 18 | /** 19 | * @notice Change the paused state of the contract 20 | * @dev Only the contract owner may call this. 21 | */ 22 | function setPaused(bool _paused) external onlyOwner { 23 | // Ensure we're actually changing the state before we do anything 24 | if (_paused == paused) { 25 | return; 26 | } 27 | 28 | // Set our paused state. 29 | paused = _paused; 30 | 31 | // If applicable, set the last pause time. 32 | if (paused) { 33 | lastPauseTime = block.timestamp; 34 | } 35 | 36 | // Let everyone know that our pause state has changed. 37 | emit PauseChanged(paused); 38 | } 39 | 40 | event PauseChanged(bool isPaused); 41 | 42 | modifier notPaused() { 43 | require(!paused, "This action cannot be performed while the contract is paused"); 44 | _; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /.github/workflows/slither.yaml: -------------------------------------------------------------------------------- 1 | name: Slither 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | branches: 9 | - "*" 10 | types: 11 | - synchronize 12 | - opened 13 | - reopened 14 | - ready_for_review 15 | 16 | jobs: 17 | slither: 18 | runs-on: ubuntu-latest 19 | strategy: 20 | matrix: 21 | include: 22 | - project: "zeta-app-contracts" 23 | file: "zeta.sarif" 24 | - project: "zevm-app-contracts" 25 | file: "zevm.sarif" 26 | permissions: 27 | contents: read 28 | security-events: write 29 | 30 | steps: 31 | - name: Checkout 32 | uses: actions/checkout@v3 33 | 34 | - name: Install Node.js 35 | uses: actions/setup-node@v2 36 | with: 37 | node-version: "18" 38 | 39 | - name: Install Dependencies 40 | run: yarn install 41 | 42 | - name: Compile contracts 43 | continue-on-error: true 44 | run: yarn compile 45 | 46 | - name: Run Slither on ${{ matrix.project}} 47 | uses: crytic/slither-action@main 48 | continue-on-error: true 49 | with: 50 | ignore-compile: true 51 | sarif: ${{ matrix.file}} 52 | node-version: "18" 53 | target: packages/${{ matrix.project}} 54 | fail-on: none 55 | 56 | - name: Upload zevm-app-contracts SARIF file 57 | uses: github/codeql-action/upload-sarif@v3 58 | with: 59 | sarif_file: ${{ matrix.file}} 60 | -------------------------------------------------------------------------------- /packages/zevm-app-contracts/test/Owned.spec.ts: -------------------------------------------------------------------------------- 1 | import { BigNumber } from "@ethersproject/bignumber"; 2 | import { parseUnits } from "@ethersproject/units"; 3 | import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; 4 | import { expect } from "chai"; 5 | import { ethers, network } from "hardhat"; 6 | 7 | import { Owned, Owned__factory } from "../typechain-types"; 8 | 9 | describe("Disperse tests", () => { 10 | let ownedContract: Owned; 11 | 12 | let accounts: SignerWithAddress[]; 13 | let deployer: SignerWithAddress; 14 | 15 | beforeEach(async () => { 16 | [deployer, ...accounts] = await ethers.getSigners(); 17 | 18 | await network.provider.send("hardhat_setBalance", [deployer.address, parseUnits("1000000").toHexString()]); 19 | 20 | const OwnedFactory = (await ethers.getContractFactory("Owned")) as Owned__factory; 21 | ownedContract = (await OwnedFactory.deploy(deployer.address)) as Owned; 22 | await ownedContract.deployed(); 23 | }); 24 | 25 | describe("Owned", () => { 26 | it("Should nominate and transfer", async () => { 27 | let owner = await ownedContract.owner(); 28 | expect(owner).to.be.eq(deployer.address); 29 | 30 | await ownedContract.nominateNewOwner(accounts[1].address); 31 | owner = await ownedContract.owner(); 32 | expect(owner).to.be.eq(deployer.address); 33 | 34 | await ownedContract.connect(accounts[1]).acceptOwnership(); 35 | 36 | owner = await ownedContract.owner(); 37 | expect(owner).to.be.eq(accounts[1].address); 38 | }); 39 | }); 40 | }); 41 | -------------------------------------------------------------------------------- /packages/zevm-app-contracts/contracts/zeta-swap/ZetaSwap.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.7; 3 | 4 | import "@zetachain/protocol-contracts/contracts/zevm/SystemContract.sol"; 5 | import "@zetachain/protocol-contracts/contracts/zevm/interfaces/zContract.sol"; 6 | 7 | import "../shared/SwapHelperLib.sol"; 8 | 9 | contract ZetaSwap is zContract { 10 | SystemContract public immutable systemContract; 11 | 12 | constructor(address systemContractAddress) { 13 | systemContract = SystemContract(systemContractAddress); 14 | } 15 | 16 | modifier onlySystem() { 17 | require(msg.sender == address(systemContract), "Only system contract can call this function"); 18 | _; 19 | } 20 | 21 | function onCrossChainCall( 22 | zContext calldata context, 23 | address zrc20, 24 | uint256 amount, 25 | bytes calldata message 26 | ) external virtual override onlySystem { 27 | (address targetZRC20, bytes32 receipient, uint256 minAmountOut) = abi.decode( 28 | message, 29 | (address, bytes32, uint256) 30 | ); 31 | uint256 outputAmount = SwapHelperLib._doSwap( 32 | systemContract.wZetaContractAddress(), 33 | systemContract.uniswapv2FactoryAddress(), 34 | systemContract.uniswapv2Router02Address(), 35 | zrc20, 36 | amount, 37 | targetZRC20, 38 | minAmountOut 39 | ); 40 | SwapHelperLib._doWithdrawal(targetZRC20, outputAmount, receipient); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /packages/zeta-app-contracts/lib/shared/deploy.helpers.ts: -------------------------------------------------------------------------------- 1 | import { BaseContract, ContractFactory } from "ethers"; 2 | import { ethers } from "hardhat"; 3 | 4 | import { ERC20, ERC20__factory as ERC20Factory } from "../../typechain-types"; 5 | 6 | export type GetContractParams = 7 | | { 8 | deployParams: Parameters; 9 | existingContractAddress?: null; 10 | } 11 | | { 12 | deployParams?: null; 13 | existingContractAddress: string; 14 | }; 15 | 16 | export const getContract = async ({ 17 | contractName, 18 | deployParams, 19 | existingContractAddress, 20 | }: GetContractParams & { contractName: string }): Promise => { 21 | const ContractFactory = (await ethers.getContractFactory(contractName)) as Factory; 22 | 23 | const isGetExistingContract = Boolean(existingContractAddress); 24 | if (isGetExistingContract) { 25 | console.log("Getting existing contract from address:", existingContractAddress); 26 | return ContractFactory.attach(existingContractAddress!) as Contract; 27 | } 28 | 29 | const contract = (await ContractFactory.deploy(...deployParams!)) as Contract; 30 | await contract.deployed(); 31 | 32 | return contract; 33 | }; 34 | 35 | export const getErc20 = async (existingContractAddress?: string) => 36 | getContract({ 37 | contractName: "ERC20", 38 | ...(existingContractAddress ? { existingContractAddress } : { deployParams: ["ERC20Mock", "ERC20Mock"] }), 39 | }); 40 | -------------------------------------------------------------------------------- /packages/zeta-app-contracts/contracts/multi-chain-value/test/ZetaConnectorMock.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.7; 3 | 4 | import "@zetachain/protocol-contracts/contracts/evm/interfaces/ZetaInterfaces.sol"; 5 | 6 | contract ZetaConnectorMockValue is ZetaConnector { 7 | event Send( 8 | uint256 destinationChainId, 9 | bytes destinationAddress, 10 | uint256 destinationGasLimit, 11 | bytes message, 12 | uint256 zetaValueAndGas, 13 | bytes zetaParams 14 | ); 15 | 16 | function send(ZetaInterfaces.SendInput calldata input) external override { 17 | emit Send( 18 | input.destinationChainId, 19 | input.destinationAddress, 20 | input.destinationGasLimit, 21 | input.message, 22 | input.zetaValueAndGas, 23 | input.zetaParams 24 | ); 25 | } 26 | 27 | function onRevert( 28 | address zetaTxSenderAddress, 29 | uint256 sourceChainId, 30 | bytes calldata destinationAddress, 31 | uint256 destinationChainId, 32 | uint256 remainingZetaValue, 33 | bytes calldata message, 34 | bytes32 internalSendHash 35 | ) external { 36 | ZetaReceiver(zetaTxSenderAddress).onZetaRevert( 37 | ZetaInterfaces.ZetaRevert( 38 | zetaTxSenderAddress, 39 | sourceChainId, 40 | destinationAddress, 41 | destinationChainId, 42 | remainingZetaValue, 43 | message 44 | ) 45 | ); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /packages/zeta-app-contracts/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@zetachain/zeta-app-contracts", 3 | "version": "0.0.6", 4 | "license": "MIT", 5 | "author": "zetachain", 6 | "publishConfig": { 7 | "access": "public", 8 | "registry": "https://registry.npmjs.org/" 9 | }, 10 | "files": [ 11 | "contracts", 12 | "data", 13 | "lib", 14 | "scripts", 15 | "typechain-types" 16 | ], 17 | "scripts": { 18 | "clean": "npx hardhat clean", 19 | "compile": "npx hardhat compile", 20 | "prepublishOnly": "echo 'This package has no prepublishOnly tasks.'", 21 | "lint:fix": "npm run lint:js:fix && npm run lint:sol:fix", 22 | "lint:js:fix": "eslint --ext .js,.ts . --fix", 23 | "lint:js": "eslint --ext .js,.ts .", 24 | "lint:sol:fix": "prettier --write \"contracts/**/*.sol\"", 25 | "lint:sol": "solhint 'contracts/**/*.sol' && prettier -c 'contracts/**/*.sol'", 26 | "lint": "npm run lint:js && npm run lint:sol", 27 | "test:watch": "echo You need to install `entr` to run this command && find contracts/**/** lib/**/** test/**/** -iname '*.sol' -o -iname '*.ts' | entr -cnr npx hardhat test", 28 | "test": "npx hardhat test", 29 | "tsc:watch": "npx tsc --watch" 30 | }, 31 | "devDependencies": { 32 | "@defi-wonderland/smock": "^2.3.4", 33 | "hardhat-gas-reporter": "^1.0.8", 34 | "solidity-coverage": "^0.7.20", 35 | "tsconfig-paths": "^3.14.1" 36 | }, 37 | "dependencies": { 38 | "@openzeppelin/contracts": "^4.9.3", 39 | "@zetachain/networks": "^4.0.0", 40 | "@zetachain/protocol-contracts": "^4.0.1", 41 | "ethers": "5.6.8" 42 | } 43 | } -------------------------------------------------------------------------------- /packages/zevm-app-contracts/test/zeta-points/UserVerificationRegistry.ts: -------------------------------------------------------------------------------- 1 | import { expect, use } from "chai"; 2 | import { solidity } from "ethereum-waffle"; 3 | use(solidity); 4 | import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; 5 | import { ethers } from "hardhat"; 6 | 7 | import { InvitationManager } from "../../typechain-types"; 8 | 9 | describe("UserVerificationRegistry Contract test", () => { 10 | let invitationManager: InvitationManager, user: SignerWithAddress, addrs: SignerWithAddress[]; 11 | 12 | beforeEach(async () => { 13 | [user, ...addrs] = await ethers.getSigners(); 14 | const InvitationManagerFactory = await ethers.getContractFactory("InvitationManager"); 15 | //@ts-ignore 16 | invitationManager = await InvitationManagerFactory.deploy(); 17 | }); 18 | 19 | describe("True", () => { 20 | it("Should be true", async () => { 21 | expect(true).to.equal(true); 22 | }); 23 | }); 24 | 25 | describe("Vereification test", () => { 26 | it("Should be able to verify a wallet", async () => { 27 | const hasBeenVerified = await invitationManager.hasBeenVerified(user.address); 28 | expect(hasBeenVerified).to.be.false; 29 | 30 | const tx = await invitationManager.markAsVerified(); 31 | const receipt = await tx.wait(); 32 | const block = await ethers.provider.getBlock(receipt.blockNumber); 33 | 34 | const hasBeenVerifiedAfter = await invitationManager.hasBeenVerified(user.address); 35 | expect(hasBeenVerifiedAfter).to.be.true; 36 | 37 | const verification = await invitationManager.getVerifiedTimestamp(user.address); 38 | expect(verification).to.be.eq(block.timestamp); 39 | }); 40 | }); 41 | }); 42 | -------------------------------------------------------------------------------- /packages/zevm-app-contracts/contracts/zeta-swap/ZetaSwapBtcInbound.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.7; 3 | 4 | import "@zetachain/protocol-contracts/contracts/zevm/SystemContract.sol"; 5 | import "@zetachain/protocol-contracts/contracts/zevm/interfaces/zContract.sol"; 6 | 7 | import "../shared/BytesHelperLib.sol"; 8 | import "../shared/SwapHelperLib.sol"; 9 | 10 | contract ZetaSwapBtcInbound is zContract { 11 | SystemContract public immutable systemContract; 12 | 13 | constructor(address systemContractAddress) { 14 | systemContract = SystemContract(systemContractAddress); 15 | } 16 | 17 | modifier onlySystem() { 18 | require(msg.sender == address(systemContract), "Only system contract can call this function"); 19 | _; 20 | } 21 | 22 | function onCrossChainCall( 23 | zContext calldata context, 24 | address zrc20, 25 | uint256 amount, 26 | bytes calldata message 27 | ) external virtual override onlySystem { 28 | address receipient = BytesHelperLib.bytesToAddress(message, 0); 29 | uint32 targetZRC20ChainId = BytesHelperLib.bytesToUint32(message, 20); 30 | address targetZRC20 = systemContract.gasCoinZRC20ByChainId(targetZRC20ChainId); 31 | 32 | uint256 outputAmount = SwapHelperLib._doSwap( 33 | systemContract.wZetaContractAddress(), 34 | systemContract.uniswapv2FactoryAddress(), 35 | systemContract.uniswapv2Router02Address(), 36 | zrc20, 37 | amount, 38 | targetZRC20, 39 | 0 40 | ); 41 | SwapHelperLib._doWithdrawal(targetZRC20, outputAmount, BytesHelperLib.addressToBytes(receipient)); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /packages/zevm-app-contracts/scripts/liquidity-incentives/deploy-token-reward.ts: -------------------------------------------------------------------------------- 1 | import { BigNumber } from "@ethersproject/bignumber"; 2 | import { isProtocolNetworkName } from "@zetachain/protocol-contracts"; 3 | import { parseEther } from "ethers/lib/utils"; 4 | import { ethers, network } from "hardhat"; 5 | 6 | import { RewardDistributorFactory__factory, SystemContract__factory } from "../../typechain-types"; 7 | import { getSystemContractAddress, getZEVMAppAddress } from "../address.helpers"; 8 | import { addReward, deployRewardByToken } from "./helpers"; 9 | 10 | const REWARD_DURATION = BigNumber.from("604800").mul(8); // 1 week * 8 11 | const REWARDS_AMOUNT = parseEther("500"); 12 | const TOKEN_ADDRESS = "0x0dee8b6e2d2035a798b67c68d47f941718a62263"; 13 | 14 | const main = async () => { 15 | const [deployer] = await ethers.getSigners(); 16 | const networkName = network.name; 17 | 18 | if (!isProtocolNetworkName(networkName)) throw new Error("Invalid network name"); 19 | 20 | const systemContractAddress = getSystemContractAddress(); 21 | const systemContract = await SystemContract__factory.connect(systemContractAddress, deployer); 22 | 23 | const factoryContractAddress = getZEVMAppAddress("rewardDistributorFactory"); 24 | 25 | const rewardDistributorFactory = RewardDistributorFactory__factory.connect(factoryContractAddress, deployer); 26 | 27 | const rewardContractAddress = await deployRewardByToken( 28 | deployer, 29 | systemContract, 30 | TOKEN_ADDRESS, 31 | rewardDistributorFactory 32 | ); 33 | await addReward(deployer, systemContract, rewardContractAddress, REWARD_DURATION, REWARDS_AMOUNT); 34 | }; 35 | 36 | main().catch((error) => { 37 | console.error(error); 38 | process.exit(1); 39 | }); 40 | -------------------------------------------------------------------------------- /packages/zevm-app-contracts/scripts/liquidity-incentives/deploy-zrc20-reward.ts: -------------------------------------------------------------------------------- 1 | import { BigNumber } from "@ethersproject/bignumber"; 2 | import { isProtocolNetworkName } from "@zetachain/protocol-contracts"; 3 | import { parseEther } from "ethers/lib/utils"; 4 | import { ethers, network } from "hardhat"; 5 | 6 | import { RewardDistributorFactory__factory, SystemContract__factory } from "../../typechain-types"; 7 | import { getSystemContractAddress, getZEVMAppAddress } from "../address.helpers"; 8 | import { addReward, deployRewardByNetwork } from "./helpers"; 9 | 10 | const REWARD_DURATION = BigNumber.from("604800").mul(8); // 1 week * 8 11 | const REWARDS_AMOUNT = parseEther("500"); 12 | const NETWORK_NAME = "goerli_testnet"; // @dev: change to your network name 13 | 14 | const main = async () => { 15 | const [deployer] = await ethers.getSigners(); 16 | const networkName = network.name; 17 | 18 | if (!isProtocolNetworkName(networkName)) throw new Error("Invalid network name"); 19 | 20 | const systemContractAddress = getSystemContractAddress(); 21 | const systemContract = await SystemContract__factory.connect(systemContractAddress, deployer); 22 | 23 | const factoryContractAddress = getZEVMAppAddress("rewardDistributorFactory"); 24 | 25 | const rewardDistributorFactory = RewardDistributorFactory__factory.connect(factoryContractAddress, deployer); 26 | 27 | const rewardContractAddress = await deployRewardByNetwork( 28 | deployer, 29 | systemContract, 30 | NETWORK_NAME, 31 | rewardDistributorFactory 32 | ); 33 | await addReward(deployer, systemContract, rewardContractAddress, REWARD_DURATION, REWARDS_AMOUNT); 34 | }; 35 | 36 | main().catch((error) => { 37 | console.error(error); 38 | process.exit(1); 39 | }); 40 | -------------------------------------------------------------------------------- /packages/zevm-app-contracts/scripts/xp-nft/deploy.ts: -------------------------------------------------------------------------------- 1 | import { isProtocolNetworkName } from "@zetachain/protocol-contracts"; 2 | import { ethers, network, upgrades } from "hardhat"; 3 | 4 | import { ZetaXP__factory } from "../../typechain-types"; 5 | import { saveAddress } from "../address.helpers"; 6 | import { verifyContract } from "../explorer.helpers"; 7 | 8 | const networkName = network.name; 9 | 10 | const name = "XP NFT"; 11 | const symbol = "XPNFT"; 12 | const ZETA_BASE_URL = "https://api.zetachain.io/nft/"; 13 | const signer = "0x1d24d94520B94B26351f6573de5ef9731c48531A"; 14 | const owner = "0x1d24d94520B94B26351f6573de5ef9731c48531A"; 15 | 16 | const deployZetaXP = async () => { 17 | if (!isProtocolNetworkName(networkName)) throw new Error("Invalid network name"); 18 | 19 | const ZetaXPFactory = (await ethers.getContractFactory("ZetaXP")) as ZetaXP__factory; 20 | const zetaXP = await upgrades.deployProxy(ZetaXPFactory, [name, symbol, ZETA_BASE_URL, signer, owner]); 21 | 22 | await zetaXP.deployed(); 23 | 24 | // Get the implementation address 25 | const implementationAddress = await upgrades.erc1967.getImplementationAddress(zetaXP.address); 26 | 27 | console.log("ZetaXP deployed to:", zetaXP.address); 28 | console.log("ZetaXP implementation deployed to:", implementationAddress); 29 | 30 | saveAddress("ZetaXP", zetaXP.address, networkName); 31 | 32 | await verifyContract(zetaXP.address, [name, symbol, ZETA_BASE_URL, signer, owner]); 33 | await verifyContract(implementationAddress, []); 34 | }; 35 | 36 | const main = async () => { 37 | if (!isProtocolNetworkName(networkName)) throw new Error("Invalid network name"); 38 | await deployZetaXP(); 39 | }; 40 | 41 | main().catch((error) => { 42 | console.error(error); 43 | process.exit(1); 44 | }); 45 | -------------------------------------------------------------------------------- /packages/zevm-app-contracts/contracts/shared/MockZRC20.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity =0.8.7; 3 | 4 | import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; 5 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 6 | 7 | import "./BytesHelperLib.sol"; 8 | 9 | contract MockZRC20 is ERC20 { 10 | address public gasFeeAddress; 11 | uint256 public gasFee; 12 | 13 | event Withdrawal(address indexed from, bytes to, uint256 value, uint256 gasfee, uint256 protocolFlatFee); 14 | 15 | constructor(uint256 initialSupply, string memory name, string memory symbol) ERC20(name, symbol) { 16 | _mint(msg.sender, initialSupply * (10 ** uint256(decimals()))); 17 | gasFeeAddress = address(this); 18 | } 19 | 20 | function setGasFeeAddress(address gasFeeAddress_) external { 21 | gasFeeAddress = gasFeeAddress_; 22 | } 23 | 24 | function setGasFee(uint256 gasFee_) external { 25 | gasFee = gasFee_; 26 | } 27 | 28 | function deposit(address to, uint256 amount) external returns (bool) { 29 | return true; 30 | } 31 | 32 | function bytesToAddress(bytes calldata data, uint256 offset, uint256 size) public pure returns (address output) { 33 | bytes memory b = data[offset:offset + size]; 34 | assembly { 35 | output := mload(add(b, size)) 36 | } 37 | } 38 | 39 | function withdraw(bytes calldata to, uint256 amount) external returns (bool) { 40 | address toAddress = BytesHelperLib.bytesToAddress(to, 12); 41 | emit Withdrawal(msg.sender, to, amount, gasFee, 0); 42 | return transfer(toAddress, amount); 43 | } 44 | 45 | function withdrawGasFee() external view returns (address, uint256) { 46 | return (gasFeeAddress, gasFee); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /packages/zevm-app-contracts/data/addresses.json: -------------------------------------------------------------------------------- 1 | { 2 | "zevm": { 3 | "zeta_testnet": { 4 | "disperse": "0x049893Bd0fC4923FC1B1136Ef2ac996C55D4942C", 5 | "rewardDistributorFactory": "0xB9dc665610CF5109cE23aBBdaAc315B41FA094c1", 6 | "zetaSwap": "0xA8168Dc495Ed61E70f5c1941e2860050AB902cEF", 7 | "zetaSwapBtcInbound": "0x358E2cfC0E16444Ba7D3164Bbeeb6bEA7472c559", 8 | "invitationManager": "0x3649C03C472B698213926543456E9c21081e529d", 9 | "withdrawERC20": "0xa349B9367cc54b47CAb8D09A95836AE8b4D1d84E", 10 | "ZetaXP": "0x5c25b6f4D2b7a550a80561d3Bf274C953aC8be7d", 11 | "InstantRewards": "0xd91164c9671C5A2ee1C95fa34A95C9141dA691D4", 12 | "ProofOfLiveness": "0x981EB6fD19717Faf293Fba0cBD05C6Ac97b8C808", 13 | "TimelockController": "0x44139C2150c11c25f517B8a8F974b59C82aEe709", 14 | "ZetaXPGov": "0x854032d484aE21acC34F36324E55A8080F21Af12", 15 | "invitationManagerV2": "0xb80f6360194Dd6B47B80bd8730b3dBb05a39e723", 16 | "InstantRewardsFactory": "0x3A557fe83FD734f21DD35E98f546B9706d486F55" 17 | }, 18 | "zeta_mainnet": { 19 | "disperse": "0x23ce409Ea60c3d75827d04D9db3d52F3af62e44d", 20 | "rewardDistributorFactory": "0xB9dc665610CF5109cE23aBBdaAc315B41FA094c1", 21 | "zetaSwap": "0xA8168Dc495Ed61E70f5c1941e2860050AB902cEF", 22 | "zetaSwapBtcInbound": "0x358E2cfC0E16444Ba7D3164Bbeeb6bEA7472c559", 23 | "invitationManager": "0x3649C03C472B698213926543456E9c21081e529d", 24 | "withdrawERC20": "0xa349B9367cc54b47CAb8D09A95836AE8b4D1d84E", 25 | "ZetaXP": "0x9A4e8bB5FFD8088ecF1DdE823e97Be8080BD38cb", 26 | "InstantRewards": "0xfD5dcBf68c81274B48593Cb4b0322e965741392b", 27 | "ProofOfLiveness": "0x327c9837B183e69C522a30E6f91A42c86e057432", 28 | "InstantRewardsFactory": "0xAf5693bBC958e442462F411F46421e389c7A8602" 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /packages/zeta-app-contracts/data/addresses.json: -------------------------------------------------------------------------------- 1 | { 2 | "ccm": { 3 | "baobab_testnet": { 4 | "multiChainSwap": "", 5 | "multiChainValue": "", 6 | "zetaTokenConsumerUniV2": "", 7 | "zetaTokenConsumerUniV3": "" 8 | }, 9 | "bsc_mainnet": { 10 | "multiChainSwap": "", 11 | "multiChainValue": "", 12 | "zetaTokenConsumerUniV2": "", 13 | "zetaTokenConsumerUniV3": "" 14 | }, 15 | "bsc_testnet": { 16 | "multiChainSwap": "", 17 | "multiChainValue": "", 18 | "zetaTokenConsumerUniV2": "", 19 | "zetaTokenConsumerUniV3": "" 20 | }, 21 | "btc_testnet": { 22 | "multiChainSwap": "", 23 | "multiChainValue": "", 24 | "zetaTokenConsumerUniV2": "", 25 | "zetaTokenConsumerUniV3": "" 26 | }, 27 | "eth_mainnet": { 28 | "multiChainSwap": "", 29 | "multiChainValue": "", 30 | "zetaTokenConsumerUniV2": "", 31 | "zetaTokenConsumerUniV3": "" 32 | }, 33 | "goerli_testnet": { 34 | "multiChainSwap": "", 35 | "multiChainValue": "", 36 | "zetaTokenConsumerUniV2": "", 37 | "zetaTokenConsumerUniV3": "" 38 | }, 39 | "mumbai_testnet": { 40 | "multiChainSwap": "", 41 | "multiChainValue": "", 42 | "zetaTokenConsumerUniV2": "", 43 | "zetaTokenConsumerUniV3": "" 44 | }, 45 | "zeta_testnet": { 46 | "multiChainSwap": "", 47 | "multiChainValue": "0x73B37B8BAbAC0e846bB2C4c581e60bFF2BFBE76e", 48 | "zetaTokenConsumerUniV2": "", 49 | "zetaTokenConsumerUniV3": "" 50 | }, 51 | "zeta_mainnet": { 52 | "multiChainSwap": "", 53 | "multiChainValue": "0xF0a3F93Ed1B126142E61423F9546bf1323Ff82DF", 54 | "zetaTokenConsumerUniV2": "", 55 | "zetaTokenConsumerUniV3": "" 56 | } 57 | }, 58 | "zevm": { 59 | "zeta_testnet": {}, 60 | "zeta_mainnet": {} 61 | } 62 | } -------------------------------------------------------------------------------- /packages/zevm-app-contracts/scripts/zeta-swap/get-system-data.ts: -------------------------------------------------------------------------------- 1 | import { ZetaProtocolNetwork } from "@zetachain/protocol-contracts"; 2 | import { ethers } from "hardhat"; 3 | 4 | import { SystemContract, SystemContract__factory } from "../../typechain-types"; 5 | import { getChainId, getGasSymbolByNetwork, getSystemContractAddress } from "../address.helpers"; 6 | 7 | const getZRC20Address = async (systemContract: SystemContract, network: ZetaProtocolNetwork) => { 8 | const tokenAddress = await systemContract.gasCoinZRC20ByChainId(getChainId(network)); 9 | console.log(`${getGasSymbolByNetwork(network)}`, tokenAddress); 10 | const tokenAddressLP = await systemContract.gasZetaPoolByChainId(getChainId(network)); 11 | console.log(`${getGasSymbolByNetwork(network)} LP`, tokenAddressLP); 12 | }; 13 | 14 | async function main() { 15 | const [deployer] = await ethers.getSigners(); 16 | const SYSTEM_CONTRACT = getSystemContractAddress(); 17 | console.log(`SYSTEM CONTRACT:`, SYSTEM_CONTRACT); 18 | 19 | const systemContract = await SystemContract__factory.connect(SYSTEM_CONTRACT, deployer); 20 | const uniswapFactoryAddress = await systemContract.uniswapv2FactoryAddress(); 21 | console.log(`uniswapv2Factory:`, uniswapFactoryAddress); 22 | const uniswapRouterAddress = await systemContract.uniswapv2Router02Address(); 23 | console.log(`uniswapv2Router02:`, uniswapRouterAddress); 24 | 25 | const WZETAAddress = await systemContract.wZetaContractAddress(); 26 | console.log(`WZETA:`, WZETAAddress); 27 | await getZRC20Address(systemContract, "btc_testnet"); 28 | await getZRC20Address(systemContract, "goerli_testnet"); 29 | await getZRC20Address(systemContract, "bsc_testnet"); 30 | await getZRC20Address(systemContract, "mumbai_testnet"); 31 | } 32 | 33 | main() 34 | .then(() => process.exit(0)) 35 | .catch((error) => { 36 | console.error(error); 37 | process.exit(1); 38 | }); 39 | -------------------------------------------------------------------------------- /packages/zevm-app-contracts/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@zetachain/zevm-app-contracts", 3 | "version": "0.0.8", 4 | "license": "MIT", 5 | "author": "zetachain", 6 | "publishConfig": { 7 | "access": "public", 8 | "registry": "https://registry.npmjs.org/" 9 | }, 10 | "files": [ 11 | "contracts", 12 | "data", 13 | "scripts", 14 | "typechain-types" 15 | ], 16 | "scripts": { 17 | "clean": "npx hardhat clean", 18 | "compile": "npx hardhat compile", 19 | "multi-chain-swap:deploy-local": "ZETA_NETWORK=troy EXECUTE_PROGRAMMATICALLY=true npx hardhat run scripts/multi-chain-swap/deploy.ts --network hardhat", 20 | "multi-chain-swap:deploy": "ZETA_NETWORK=athens EXECUTE_PROGRAMMATICALLY=true npx hardhat run scripts/multi-chain-swap/deploy.ts", 21 | "lint:fix": "npm run lint:js:fix && npm run lint:sol:fix", 22 | "lint:js:fix": "eslint --ext .js,.ts . --fix", 23 | "lint:js": "eslint --ext .js,.ts .", 24 | "lint:sol:fix": "prettier --write \"contracts/**/*.sol\"", 25 | "lint:sol": "solhint 'contracts/**/*.sol' && prettier -c 'contracts/**/*.sol'", 26 | "lint": "npm run lint:js && npm run lint:sol", 27 | "test:watch": "echo You need to install `entr` to run this command && find contracts/**/** lib/**/** test/**/** -iname '*.sol' -o -iname '*.ts' | entr -cnr npx hardhat test", 28 | "test": "npx hardhat test", 29 | "tsc:watch": "npx tsc --watch" 30 | }, 31 | "devDependencies": { 32 | "hardhat-gas-reporter": "^1.0.8", 33 | "solidity-coverage": "^0.7.20", 34 | "tsconfig-paths": "^3.14.1" 35 | }, 36 | "dependencies": { 37 | "@openzeppelin/contracts": "4.9.3", 38 | "@openzeppelin/contracts-upgradeable": "4.9.3", 39 | "@openzeppelin/hardhat-upgrades": "^1.7.0-rc.0", 40 | "@uniswap/v2-periphery": "1.1.0-beta.0", 41 | "@zetachain/networks": "^4.0.0", 42 | "@zetachain/protocol-contracts": "^4.0.1" 43 | } 44 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ZetaChain App Contracts 2 | 3 | Contracts of official applications deployed by the core team to ZetaChain. 4 | 5 | ## Learn more about ZetaChain 6 | 7 | * Check our [website](https://www.zetachain.com/). 8 | * Read our [docs](https://docs.zetachain.com/). 9 | 10 | 11 | 12 | ## Usage 13 | 14 | 1. Install [Node.js LTS](https://nodejs.org/en/) (previous versions may, but are not guaranteed to work). 15 | 16 | 1. Install `yarn` (make sure NPM has the right permissions to add global packages): 17 | 18 | npm i -g yarn 19 | 20 | 1. Install the dependencies: 21 | 22 | yarn 23 | 24 | 1. From the root folder, compile the contracts: 25 | 26 | yarn compile 27 | 28 | ### Packages 29 | 30 | #### [Zeta App contracts](packages/zeta-app-contracts) 31 | 32 | #### [ZEVM App contracts](packages/zevm-app-contracts) 33 | 34 | ### Cross-repo commands 35 | 36 | #### Package-specific commands 37 | 38 | They run independently, only on the packages that implement them: 39 | 40 | ```bash 41 | yarn compile 42 | ``` 43 | 44 | ```bash 45 | yarn clean 46 | ``` 47 | 48 | ```bash 49 | yarn test 50 | ``` 51 | 52 | #### Repo commands 53 | 54 | They run once, across the whole repo: 55 | 56 | ```bash 57 | yarn lint 58 | ``` 59 | 60 | ```bash 61 | yarn lint:fix 62 | ``` 63 | 64 | ## Coverage 65 | To check the test coverage run the follow command on the desire package 66 | 67 | ```bash 68 | npx hardhat coverage 69 | ``` 70 | 71 | ## Static test 72 | We run slither on our packages. If you want to run it should install slither 73 | 74 | ```bash 75 | brew install slither-analyzer 76 | ``` 77 | and execute it 78 | 79 | ```bash 80 | slither . --filter-paths "contracts/test/|node_modules/" --exclude naming-convention 81 | ``` 82 | 83 | ## Contributing 84 | 85 | We welcome (and appreciate) everyone's contributions. If you wanna contribute, read [CONTRIBUTING.md](CONTRIBUTING.md) for next steps. 86 | 87 | -------------------------------------------------------------------------------- /bugbounty.md: -------------------------------------------------------------------------------- 1 | ## Bug Bounty Overview 2 | 3 | ZetaChain is committed to security across all aspects of its ecosystem. To that end, ZetaChain has established a bug bounty program to reward researchers, developers, and users who help identify and report security vulnerabilities. 4 | 5 | You can access and report issues at [https://immunefi.com/bounty/zetachain/](https://immunefi.com/bounty/zetachain/). 6 | 7 | ## Scope 8 | 9 | The scope of this bug bounty program is focused on ZetaChain's smart contracts, public-facing APIs, blockchain protocol/infrastructure, and web applications. 10 | 11 | ## Program Guidelines 12 | 13 | 1. All reports must be submitted through Immunefi, accessible [here](https://immunefi.com/bounty/zetachain/). 14 | 2. Report any suspected vulnerability promptly. 15 | 3. Do not attempt to exploit a vulnerability without prior authorization. 16 | 4. Do not publicly disclose a vulnerability before it is reported and patched. 17 | 5. Do not access data or systems beyond the scope of the vulnerability. 18 | 6. Do not use social engineering techniques. 19 | 7. Do not attempt to access accounts or personal data of users. 20 | 21 | ## Rewards 22 | 23 | The rewards for successful vulnerability reports range from $5,000 to $100,000, depending on the severity of the issue. All payouts are to be done by the ZetaChain team through Immunefi. 24 | 25 | ### **Smart Contracts** 26 | 27 | | Critical | USD $30,000 to $100,000 | 28 | | --- | --- | 29 | | High | USD $10,000 to $30,000 | 30 | | Medium | USD $10,000 | 31 | 32 | ### **Websites and Applications** 33 | 34 | | Critical | USD $15,000 to $30,000 | 35 | | --- | --- | 36 | | High | USD $5,000 to $15,000 | 37 | | Medium | USD $5,000 | 38 | 39 | ## Responsible Disclosure 40 | 41 | We value responsible disclosure, and we encourage all participants to act responsibly when reporting vulnerabilities. 42 | 43 | ## Contact 44 | 45 | For any questions or concerns, please contact us at bugbounty@zetachain.com. 46 | -------------------------------------------------------------------------------- /packages/zevm-app-contracts/scripts/zeta-swap/helpers.ts: -------------------------------------------------------------------------------- 1 | import { BigNumber } from "@ethersproject/bignumber"; 2 | import { HashZero } from "@ethersproject/constants"; 3 | import { ethers } from "hardhat"; 4 | 5 | export const encodeParams = (dataTypes: any[], data: any[]) => { 6 | const abiCoder = ethers.utils.defaultAbiCoder; 7 | return abiCoder.encode(dataTypes, data); 8 | }; 9 | 10 | export const getSwapParams = (destination: string, destinationToken: string, minOutput: BigNumber) => { 11 | const paddedDestination = ethers.utils.hexlify(ethers.utils.zeroPad(destination, 32)); 12 | const params = encodeParams(["address", "bytes32", "uint256"], [destinationToken, paddedDestination, minOutput]); 13 | 14 | return params; 15 | }; 16 | 17 | export const getSwapData = (zetaSwap: string, destination: string, destinationToken: string, minOutput: BigNumber) => { 18 | const params = getSwapParams(destination, destinationToken, minOutput); 19 | 20 | return `${zetaSwap}${params.slice(2)}`; 21 | }; 22 | 23 | export const getBitcoinTxMemo = (zetaSwapAddress: string, destinationAddress: string, chainId: string) => { 24 | const paddedHexChainId = ethers.utils.hexlify(Number(chainId)).slice(2).padStart(8, "0"); 25 | const rawMemo = `${zetaSwapAddress}${destinationAddress.slice(2)}${paddedHexChainId}`; 26 | return ethers.utils.base64.encode(rawMemo); 27 | }; 28 | 29 | export const getBitcoinTxMemoForTest = (destinationAddress: string, chainId: string) => { 30 | const paddedHexChainId = ethers.utils.hexlify(Number(chainId)).slice(2).padStart(8, "0"); 31 | const rawMemo = `${destinationAddress.slice(2)}${paddedHexChainId}`; 32 | 33 | const paddedMemo = rawMemo.padEnd(HashZero.length - 2, "0"); 34 | return `0x${paddedMemo}`; 35 | }; 36 | 37 | export const getMultiOutputForTest = (destinationAddress: string) => { 38 | const rawMemo = `${destinationAddress.slice(2)}`; 39 | 40 | const paddedMemo = rawMemo.padEnd(HashZero.length - 2, "0"); 41 | return `0x${paddedMemo}`; 42 | }; 43 | -------------------------------------------------------------------------------- /packages/zevm-app-contracts/contracts/instant-rewards/InstantRewardsFactory.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.20; 3 | 4 | import "@openzeppelin/contracts/access/Ownable.sol"; 5 | import "./InstantRewardsV2.sol"; 6 | 7 | contract InstantRewardsFactory is Ownable { 8 | bool public allowPublicCreation = false; 9 | 10 | error AccessDenied(); 11 | error InvalidSignerAddress(); 12 | error EmptyName(); 13 | error StartTimeInPast(); 14 | error EndTimeBeforeStart(); 15 | 16 | event InstantRewardsCreated(address indexed instantRewards, address indexed owner, string name); 17 | 18 | constructor(address owner) Ownable() { 19 | transferOwnership(owner); 20 | } 21 | 22 | function setAllowPublicCreation(bool allowPublicCreation_) external onlyOwner { 23 | allowPublicCreation = allowPublicCreation_; 24 | } 25 | 26 | function createInstantRewards( 27 | address signerAddress, 28 | uint256 start, 29 | uint256 end, 30 | string memory name, 31 | string memory promoUrl, 32 | string memory avatarUrl, 33 | string memory description 34 | ) external returns (address) { 35 | if (signerAddress == address(0)) revert InvalidSignerAddress(); 36 | if (bytes(name).length == 0) revert EmptyName(); 37 | if (start < block.timestamp) revert StartTimeInPast(); 38 | if (end <= start) revert EndTimeBeforeStart(); 39 | 40 | bool isOwner = owner() == msg.sender; 41 | if (!allowPublicCreation && !isOwner) revert AccessDenied(); 42 | 43 | InstantRewardsV2 instantRewards = new InstantRewardsV2( 44 | signerAddress, 45 | owner(), 46 | start, 47 | end, 48 | name, 49 | promoUrl, 50 | avatarUrl, 51 | description 52 | ); 53 | emit InstantRewardsCreated(address(instantRewards), owner(), name); 54 | return address(instantRewards); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /packages/zeta-app-contracts/scripts/multi-chain-value/deterministic-deploy.ts: -------------------------------------------------------------------------------- 1 | import { getAddress, isProtocolNetworkName } from "@zetachain/protocol-contracts"; 2 | import { BigNumber } from "ethers"; 3 | import { ethers, network } from "hardhat"; 4 | 5 | import { deployContractToAddress, saltToHex } from "../../lib/shared/ImmutableCreate2Factory.helpers"; 6 | import { MultiChainValue__factory } from "../../typechain-types"; 7 | import { saveAddress } from "../address.helpers"; 8 | 9 | const DEPLOYER_ADDRESS = process.env.DEPLOYER_ADDRESS ?? ""; 10 | const SALT_NUMBER = "0"; 11 | 12 | const networkName = network.name; 13 | 14 | export async function deterministicDeployMultiChainValue() { 15 | if (!isProtocolNetworkName(networkName)) throw new Error("Invalid network name"); 16 | 17 | const accounts = await ethers.getSigners(); 18 | const [signer] = accounts; 19 | 20 | const saltNumber = SALT_NUMBER; 21 | const saltStr = BigNumber.from(saltNumber).toHexString(); 22 | 23 | const connector = getAddress("connector", networkName); 24 | const zetaToken = getAddress("zetaToken", networkName); 25 | 26 | const immutableCreate2Factory = getAddress("immutableCreate2Factory", networkName); 27 | 28 | const salthex = saltToHex(saltStr, DEPLOYER_ADDRESS); 29 | 30 | const constructorTypes = ["address", "address"]; 31 | const constructorArgs = [connector, zetaToken]; 32 | 33 | const contractBytecode = MultiChainValue__factory.bytecode; 34 | 35 | const { address } = await deployContractToAddress({ 36 | constructorArgs, 37 | constructorTypes, 38 | contractBytecode, 39 | factoryAddress: immutableCreate2Factory, 40 | salt: salthex, 41 | signer, 42 | }); 43 | 44 | saveAddress("multiChainValue", address); 45 | console.log("Deployed multiChainValue. Address:", address); 46 | } 47 | 48 | if (!process.env.EXECUTE_PROGRAMMATICALLY) { 49 | deterministicDeployMultiChainValue() 50 | .then(() => process.exit(0)) 51 | .catch((error) => { 52 | console.error(error); 53 | process.exit(1); 54 | }); 55 | } 56 | -------------------------------------------------------------------------------- /packages/zevm-app-contracts/scripts/uniswap/uniswap.helpers.ts: -------------------------------------------------------------------------------- 1 | import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; 2 | import { formatUnits, parseUnits } from "ethers/lib/utils"; 3 | import { ethers } from "hardhat"; 4 | 5 | import { IUniswapV2Factory__factory } from "../../typechain-types"; 6 | import { ERC20, IUniswapV2Pair__factory } from "../../typechain-types"; 7 | 8 | export interface Pair { 9 | TokenA: string; 10 | TokenB: string; 11 | } 12 | 13 | export const getNow = async () => { 14 | const block = await ethers.provider.getBlock("latest"); 15 | return block.timestamp; 16 | }; 17 | 18 | export const sortPair = (token1: string, token2: string): Pair => { 19 | if (token1 < token2) { 20 | return { TokenA: token1, TokenB: token2 }; 21 | } 22 | return { TokenA: token2, TokenB: token1 }; 23 | }; 24 | 25 | export const printReserves = async ( 26 | tokenContract: ERC20, 27 | WZETAAddress: string, 28 | uniswapFactoryAddress: string, 29 | deployer: SignerWithAddress 30 | ) => { 31 | const uniswapV2Factory = IUniswapV2Factory__factory.connect(uniswapFactoryAddress, deployer); 32 | 33 | const pair = sortPair(tokenContract.address, WZETAAddress); 34 | 35 | const poolAddress = await uniswapV2Factory.getPair(pair.TokenA, pair.TokenB); 36 | 37 | const pool = IUniswapV2Pair__factory.connect(poolAddress, deployer); 38 | 39 | const reserves = await pool.getReserves(); 40 | 41 | const reservesZETA = WZETAAddress < tokenContract.address ? reserves.reserve0 : reserves.reserve1; 42 | const reservesToken = WZETAAddress > tokenContract.address ? reserves.reserve0 : reserves.reserve1; 43 | 44 | const tokenDecimals = await tokenContract.decimals(); 45 | const reservesToken18Decimals = 46 | 18 === tokenDecimals ? reservesToken : reservesToken.mul(parseUnits("1", 18 - tokenDecimals)); 47 | 48 | const ratio = reservesToken18Decimals.mul(parseUnits("1")).div(reservesZETA); 49 | 50 | console.log( 51 | `Reserves ZETA: ${formatUnits(reservesZETA)} / TOKEN: ${formatUnits(reservesToken18Decimals)} / Ratio ${formatUnits( 52 | ratio 53 | )}` 54 | ); 55 | }; 56 | -------------------------------------------------------------------------------- /packages/zevm-app-contracts/contracts/instant-rewards/InstantRewardsV2.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.20; 3 | 4 | import "./InstantRewards.sol"; 5 | 6 | contract InstantRewardsV2 is InstantRewards { 7 | string public name; 8 | 9 | uint256 public start; 10 | uint256 public end; 11 | string public promoUrl; 12 | string public avatarUrl; 13 | string public description; 14 | 15 | event TimeframeUpdated(uint256 start, uint256 end); 16 | 17 | error InvalidTimeframe(); 18 | error InstantRewardNotActive(); 19 | error InstantRewardStillActive(); 20 | 21 | constructor( 22 | address signerAddress_, 23 | address owner, 24 | uint256 start_, 25 | uint256 end_, 26 | string memory name_, 27 | string memory promoUrl_, 28 | string memory avatarUrl_, 29 | string memory description_ 30 | ) InstantRewards(signerAddress_, owner) { 31 | if (signerAddress_ == address(0)) revert InvalidAddress(); 32 | if (start_ > end_) revert InvalidTimeframe(); 33 | start = start_; 34 | end = end_; 35 | name = name_; 36 | promoUrl = promoUrl_; 37 | avatarUrl = avatarUrl_; 38 | description = description_; 39 | } 40 | 41 | function isActive() public view returns (bool) { 42 | return block.timestamp >= start && block.timestamp <= end; 43 | } 44 | 45 | function setTimeframe(uint256 start_, uint256 end_) external onlyOwner { 46 | if (start_ > end_) revert InvalidTimeframe(); 47 | if (start_ < block.timestamp || end_ < block.timestamp) revert InvalidTimeframe(); 48 | if (isActive()) revert InstantRewardStillActive(); 49 | start = start_; 50 | end = end_; 51 | emit TimeframeUpdated(start_, end_); 52 | } 53 | 54 | function claim(ClaimData memory claimData) public override { 55 | if (!isActive()) revert InstantRewardNotActive(); 56 | super.claim(claimData); 57 | } 58 | 59 | function withdraw(address wallet, uint256 amount) public override onlyOwner { 60 | super.withdraw(wallet, amount); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /packages/zeta-app-contracts/docs/multi-chain-value.md: -------------------------------------------------------------------------------- 1 | # Multi Chain Value 2 | 3 | [![Docs](https://img.shields.io/badge/Zeta%20docs-🔗-43ad51)](https://docs.zetachain.com/develop/examples/multi-chain-value-transfer) 4 | 5 | Transfer value across chains using [Zeta Connector](https://docs.zetachain.com/reference/connector). 6 | 7 | ## Try it in testnet 8 | 9 | This example is currently deployed to [BSC Testnet](https://testnet.bscscan.com/address/0x0E396e23cAD688c0e5252B20dFeAcC248b0d8B01) and [Goerli](https://goerli.etherscan.io/address/0x8bA6c6047AA5a55C2Ce10615b1D358Cb4B9D27f6), the contracts are verified so you can use BSCScan and Etherscan to play with them. 10 | 11 | ### Doing a cross-chain value transfer 12 | 13 | 1. Get some [testnet Zeta](https://docs.zetachain.com/develop/get-testnet-zeta). You may have to manually add the ZETA token to your wallet. The token addresses can be found [here](https://docs.zetachain.com/develop/development-addresses). 14 | 1. Go to the [BSC Testnet ZETA token contract's write methods](https://testnet.bscscan.com/address/0x6Cc37160976Bbd1AecB5Cce4C440B28e883c7898#writeContract). 15 | 1. Connect your wallet. 16 | 1. Give allowance to the multi-chain value contract to spend your ZETA: `0x0E396e23cAD688c0e5252B20dFeAcC248b0d8B01`. Use the methods `approve()` or `increaseAllowance()` 17 | 1. Go to the [BSC Testnet multi-chain value contract's write methods](https://testnet.bscscan.com/address/0x0E396e23cAD688c0e5252B20dFeAcC248b0d8B01#writeContract). 18 | 1. Connect your wallet. 19 | 1. You will need some tBNB to perform the transfer step. You can get some from [this faucet](https://testnet.binance.org/faucet-smart). 20 | 1. Transfer (up to) the amount you previously approved of ZETA to Goerli (chain id 5) by calling `send()` in the write contract, passing in your wallet's address as the `destinationAddress`. *Make sure to convert the destinationAddress to bytes. You can use [this code snippet](https://stackblitz.com/edit/typescript-bwhh4c?file=index.ts).* Use [eth converter](https://eth-converter.com/) to express the amount in wei. 21 | 1. Check the receiver address balance in Goerli. *Note that the cross-chain transfer may take around 1 minute.* 22 | -------------------------------------------------------------------------------- /packages/zevm-app-contracts/scripts/zeta-swap/swap-on-zevm.ts: -------------------------------------------------------------------------------- 1 | import { BigNumber } from "@ethersproject/bignumber"; 2 | import { parseUnits } from "@ethersproject/units"; 3 | import { getZRC20Address, isProtocolNetworkName } from "@zetachain/protocol-contracts"; 4 | import { ethers } from "hardhat"; 5 | import { network } from "hardhat"; 6 | 7 | import { ERC20__factory, ZetaSwap__factory, ZetaSwapBtcInbound__factory } from "../../typechain-types"; 8 | import { getZEVMAppAddress } from "../address.helpers"; 9 | import { getSwapParams } from "./helpers"; 10 | const networkName = network.name; 11 | 12 | const USE_BTC_SWAP = true; 13 | const SAMPLE_MEMO = "0x25A92a5853702F199bb2d805Bba05d67025214A800000005"; // 0xADDRESS + FFFF chain id (05 for goerli) 14 | 15 | const main = async () => { 16 | if (!isProtocolNetworkName(networkName)) throw new Error("Invalid network name"); 17 | const [signer] = await ethers.getSigners(); 18 | 19 | const zetaSwap = getZEVMAppAddress(USE_BTC_SWAP ? "zetaSwapBtcInbound" : "zetaSwap"); 20 | 21 | const amount = parseUnits("0.00001", 8); 22 | const sourceToken = getZRC20Address("btc_testnet"); 23 | 24 | const zrc20Contract = ERC20__factory.connect(sourceToken, signer); 25 | const tx0 = await zrc20Contract.transfer(zetaSwap, amount); 26 | await tx0.wait(); 27 | 28 | console.log(`Swapping native token from zEVM...`); 29 | 30 | let params; 31 | let zetaSwapContract; 32 | 33 | if (USE_BTC_SWAP) { 34 | params = ethers.utils.arrayify(SAMPLE_MEMO); 35 | zetaSwapContract = ZetaSwapBtcInbound__factory.connect(zetaSwap, signer); 36 | } else { 37 | params = getSwapParams(signer.address, getZRC20Address("goerli_testnet"), BigNumber.from("0")); 38 | zetaSwapContract = ZetaSwap__factory.connect(zetaSwap, signer); 39 | } 40 | const zContextStruct = { 41 | chainID: ethers.BigNumber.from("0"), 42 | origin: ethers.constants.HashZero, 43 | sender: ethers.constants.AddressZero, 44 | }; 45 | const tx1 = await zetaSwapContract.onCrossChainCall(zContextStruct, sourceToken, amount, params); 46 | await tx1.wait(); 47 | 48 | console.log("tx:", tx1.hash); 49 | }; 50 | 51 | main().catch((error) => { 52 | console.error(error); 53 | process.exit(1); 54 | }); 55 | -------------------------------------------------------------------------------- /.github/workflows/publish-npm.yaml: -------------------------------------------------------------------------------- 1 | name: Publish to NPM 2 | 3 | on: 4 | release: 5 | types: [published] 6 | 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | 11 | steps: 12 | - name: Checkout Repository 13 | uses: actions/checkout@v3 14 | 15 | - name: Setup Node.js 16 | uses: actions/setup-node@v3 17 | with: 18 | node-version: "16" 19 | registry-url: "https://registry.npmjs.org" 20 | 21 | - name: Install Dependencies 22 | run: yarn install 23 | 24 | - name: Determine NPM Tag zeta app contracts 25 | id: determine-npm-tag-zeta-app-contracts 26 | working-directory: packages/zeta-app-contracts 27 | run: | 28 | VERSION_TAG=${GITHUB_REF#refs/tags/v} 29 | if [[ $VERSION_TAG == *"-"* ]]; then 30 | echo ::set-output name=NPM_TAG::${VERSION_TAG#*-} 31 | else 32 | echo ::set-output name=NPM_TAG::latest 33 | fi 34 | env: 35 | GITHUB_REF: ${{ github.ref }} 36 | 37 | - name: Publish to NPM zeta app contracts 38 | working-directory: packages/zeta-app-contracts 39 | run: npm publish --tag ${{ steps.determine-npm-tag-zeta-app-contracts.outputs.NPM_TAG }} 40 | env: 41 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 42 | GITHUB_REF: ${{ github.ref }} 43 | continue-on-error: true 44 | 45 | - name: Determine NPM Tag zevm app contracts 46 | id: determine-npm-tag-zevm-app-contracts 47 | working-directory: packages/zevm-app-contracts 48 | run: | 49 | VERSION_TAG=${GITHUB_REF#refs/tags/v} 50 | if [[ $VERSION_TAG == *"-"* ]]; then 51 | echo ::set-output name=NPM_TAG::${VERSION_TAG#*-} 52 | else 53 | echo ::set-output name=NPM_TAG::latest 54 | fi 55 | env: 56 | GITHUB_REF: ${{ github.ref }} 57 | 58 | - name: Publish to NPM zevm app contracts 59 | working-directory: packages/zevm-app-contracts 60 | run: npm publish --tag ${{ steps.determine-npm-tag-zevm-app-contracts.outputs.NPM_TAG }} 61 | env: 62 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 63 | GITHUB_REF: ${{ github.ref }} 64 | -------------------------------------------------------------------------------- /packages/zeta-app-contracts/scripts/multi-chain-value/deploy.ts: -------------------------------------------------------------------------------- 1 | import { getAddress, isProtocolNetworkName, isTestnetNetwork } from "@zetachain/protocol-contracts/dist/lib"; 2 | import { ethers, network } from "hardhat"; 3 | 4 | import { MultiChainValue, MultiChainValue__factory } from "../../typechain-types"; 5 | import { getChainId, saveAddress } from "../address.helpers"; 6 | 7 | const networkName = network.name; 8 | 9 | async function main() { 10 | if (!isProtocolNetworkName(networkName)) throw new Error("Invalid network name"); 11 | 12 | const connectorAddress = getAddress("connector", networkName); 13 | const zetaTokenAddress = getAddress("zetaToken", networkName); 14 | 15 | const Factory = (await ethers.getContractFactory("MultiChainValue")) as MultiChainValue__factory; 16 | const contract = (await Factory.deploy(connectorAddress, zetaTokenAddress)) as MultiChainValue; 17 | await contract.deployed(); 18 | 19 | console.log("MultiChainValue deployed to:", contract.address); 20 | saveAddress("multiChainValue", contract.address); 21 | 22 | console.log("MultiChainValue post rutine..."); 23 | 24 | //@ts-ignore 25 | const isTestnet = isTestnetNetwork(networkName); 26 | 27 | if (isTestnet) { 28 | await (await contract.addAvailableChainId(getChainId("goerli_testnet"))).wait().catch((e: any) => console.error(e)); 29 | await (await contract.addAvailableChainId(getChainId("bsc_testnet"))).wait().catch((e: any) => console.error(e)); 30 | await (await contract.addAvailableChainId(getChainId("zeta_testnet"))).wait().catch((e: any) => console.error(e)); 31 | await (await contract.addAvailableChainId(getChainId("mumbai_testnet"))).wait().catch((e: any) => console.error(e)); 32 | } else { 33 | await (await contract.addAvailableChainId(getChainId("bsc_mainnet"))).wait().catch((e: any) => console.error(e)); 34 | await (await contract.addAvailableChainId(getChainId("zeta_mainnet"))).wait().catch((e: any) => console.error(e)); 35 | await (await contract.addAvailableChainId(getChainId("eth_mainnet"))).wait().catch((e: any) => console.error(e)); 36 | } 37 | 38 | console.log("MultiChainValue post rutine finish"); 39 | } 40 | 41 | main().catch((error) => { 42 | console.error(error); 43 | process.exit(1); 44 | }); 45 | -------------------------------------------------------------------------------- /packages/zevm-app-contracts/test/Synthetixio/common.js: -------------------------------------------------------------------------------- 1 | const { web3 } = require("hardhat"); 2 | 3 | const { assert } = require("chai"); 4 | 5 | const { 6 | assertEventEqual, 7 | assertEventsEqual, 8 | assertBNEqual, 9 | assertBNNotEqual, 10 | assertBNClose, 11 | assertBNGreaterEqualThan, 12 | assertBNGreaterThan, 13 | assertBNLessEqualThan, 14 | assertBNLessThan, 15 | assertDeepEqual, 16 | assertInvalidOpcode, 17 | assertUnitEqual, 18 | assertUnitNotEqual, 19 | assertRevert, 20 | fromUnit, 21 | takeSnapshot, 22 | restoreSnapshot, 23 | } = require("./utils")(); 24 | 25 | let lastSnapshotId; 26 | 27 | module.exports = { 28 | addSnapshotBeforeRestoreAfter() { 29 | before(async () => { 30 | lastSnapshotId = await takeSnapshot(); 31 | }); 32 | 33 | after(async () => { 34 | await restoreSnapshot(lastSnapshotId); 35 | }); 36 | }, 37 | 38 | // And this is our test sandboxing. It snapshots and restores between each test. 39 | // Note: if a test suite uses fastForward at all, then it MUST also use these snapshots, 40 | // otherwise it will update the block time of the EVM and future tests that expect a 41 | // starting timestamp will fail. 42 | addSnapshotBeforeRestoreAfterEach() { 43 | beforeEach(async () => { 44 | lastSnapshotId = await takeSnapshot(); 45 | }); 46 | 47 | afterEach(async () => { 48 | await restoreSnapshot(lastSnapshotId); 49 | }); 50 | }, 51 | 52 | // So we don't have to constantly import our assert helpers everywhere 53 | // we'll just tag them onto the assert object for easy access. 54 | assert: Object.assign({}, assert, { 55 | bnClose: assertBNClose, 56 | bnEqual: assertBNEqual, 57 | bnGt: assertBNGreaterThan, 58 | bnGte: assertBNGreaterEqualThan, 59 | bnLt: assertBNLessThan, 60 | bnLte: assertBNLessEqualThan, 61 | bnNotEqual: assertBNNotEqual, 62 | deepEqual: assertDeepEqual, 63 | etherEqual: assertUnitEqual, 64 | etherNotEqual: assertUnitNotEqual, 65 | eventEqual: assertEventEqual, 66 | eventsEqual: assertEventsEqual, 67 | invalidOpcode: assertInvalidOpcode, 68 | revert: assertRevert, 69 | unitEqual: assertUnitEqual, 70 | unitNotEqual: assertUnitNotEqual, 71 | }), 72 | }; 73 | -------------------------------------------------------------------------------- /packages/zevm-app-contracts/test/xp-nft/test.helpers.ts: -------------------------------------------------------------------------------- 1 | import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; 2 | 3 | export interface NFT { 4 | tag: string; 5 | to: string; 6 | tokenId: number | undefined; 7 | } 8 | 9 | export interface NFTSigned extends NFT { 10 | sigTimestamp: number; 11 | signature: string; 12 | signatureExpiration: number; 13 | } 14 | 15 | export const getSignature = async ( 16 | chainId: number, 17 | verifyingContract: string, 18 | signer: SignerWithAddress, 19 | signatureExpiration: number, 20 | timestamp: number, 21 | to: string, 22 | nft: NFT 23 | ) => { 24 | const domain = { 25 | chainId: chainId, 26 | name: "ZetaXP", 27 | verifyingContract: verifyingContract, 28 | version: "1", 29 | }; 30 | 31 | const types = { 32 | MintOrUpdateNFT: [ 33 | { name: "to", type: "address" }, 34 | { name: "signatureExpiration", type: "uint256" }, 35 | { name: "sigTimestamp", type: "uint256" }, 36 | { name: "tag", type: "bytes32" }, 37 | ], 38 | }; 39 | 40 | const value = { 41 | sigTimestamp: timestamp, 42 | signatureExpiration, 43 | tag: nft.tag, 44 | to, 45 | }; 46 | // Signing the data 47 | const signature = await signer._signTypedData(domain, types, value); 48 | return signature; 49 | }; 50 | 51 | export const getSelLevelSignature = async ( 52 | chainId: number, 53 | verifyingContract: string, 54 | signer: SignerWithAddress, 55 | signatureExpiration: number, 56 | timestamp: number, 57 | tokenId: number, 58 | level: number 59 | ) => { 60 | const domain = { 61 | chainId: chainId, 62 | name: "ZetaXP", 63 | verifyingContract: verifyingContract, 64 | version: "1", 65 | }; 66 | 67 | const types = { 68 | SetLevel: [ 69 | { name: "tokenId", type: "uint256" }, 70 | { name: "signatureExpiration", type: "uint256" }, 71 | { name: "sigTimestamp", type: "uint256" }, 72 | { name: "level", type: "uint256" }, 73 | ], 74 | }; 75 | 76 | const value = { 77 | level, 78 | sigTimestamp: timestamp, 79 | signatureExpiration, 80 | tokenId, 81 | }; 82 | // Signing the data 83 | const signature = await signer._signTypedData(domain, types, value); 84 | return signature; 85 | }; 86 | -------------------------------------------------------------------------------- /packages/zevm-app-contracts/contracts/xp-nft/xpNFT_V2.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.20; 3 | 4 | import "./xpNFT.sol"; 5 | 6 | contract ZetaXP_V2 is ZetaXP { 7 | bytes32 internal constant SETLEVEL_TYPEHASH = 8 | keccak256("SetLevel(uint256 tokenId,uint256 signatureExpiration,uint256 sigTimestamp,uint256 level)"); 9 | 10 | struct SetLevelData { 11 | uint256 tokenId; 12 | bytes signature; 13 | uint256 signatureExpiration; 14 | uint256 sigTimestamp; 15 | uint256 level; 16 | } 17 | 18 | mapping(uint256 => uint256) public levelByTokenId; 19 | 20 | // Event for Level Set 21 | event LevelSet(address indexed sender, uint256 indexed tokenId, uint256 level); 22 | 23 | function version() public pure virtual override returns (string memory) { 24 | return "2.0.0"; 25 | } 26 | 27 | function _verifyUpdateNFTSignature(uint256 tokenId, UpdateData memory updateData) internal view { 28 | _verify(tokenId, updateData); 29 | } 30 | 31 | function _verifySetLevelSignature(SetLevelData memory data) internal view { 32 | bytes32 structHash = keccak256( 33 | abi.encode(SETLEVEL_TYPEHASH, data.tokenId, data.signatureExpiration, data.sigTimestamp, data.level) 34 | ); 35 | bytes32 constructedHash = _hashTypedDataV4(structHash); 36 | 37 | if (!SignatureChecker.isValidSignatureNow(signerAddress, constructedHash, data.signature)) { 38 | revert InvalidSigner(); 39 | } 40 | 41 | if (block.timestamp > data.signatureExpiration) revert SignatureExpired(); 42 | if (data.sigTimestamp <= lastUpdateTimestampByTokenId[data.tokenId]) revert OutdatedSignature(); 43 | } 44 | 45 | function setLevel(SetLevelData memory data) external { 46 | _verifySetLevelSignature(data); 47 | 48 | levelByTokenId[data.tokenId] = data.level; 49 | lastUpdateTimestampByTokenId[data.tokenId] = data.sigTimestamp; 50 | emit LevelSet(msg.sender, data.tokenId, data.level); 51 | } 52 | 53 | function getLevel(uint256 tokenId) external view returns (uint256) { 54 | return levelByTokenId[tokenId]; 55 | } 56 | 57 | function totalSupply() external view returns (uint256) { 58 | return _currentTokenId - 1; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /packages/zeta-app-contracts/hardhat.config.ts: -------------------------------------------------------------------------------- 1 | import "@nomicfoundation/hardhat-verify"; 2 | import "@nomiclabs/hardhat-waffle"; 3 | import "@typechain/hardhat"; 4 | import "hardhat-gas-reporter"; 5 | import "solidity-coverage"; 6 | import "tsconfig-paths/register"; 7 | 8 | import { getHardhatConfigNetworks } from "@zetachain/networks"; 9 | import * as dotenv from "dotenv"; 10 | import type { HardhatUserConfig } from "hardhat/types"; 11 | 12 | dotenv.config(); 13 | 14 | const PRIVATE_KEYS = process.env.PRIVATE_KEY !== undefined ? [`0x${process.env.PRIVATE_KEY}`] : []; 15 | 16 | const config: HardhatUserConfig = { 17 | //@ts-ignore 18 | etherscan: { 19 | apiKey: { 20 | // BSC 21 | bsc: process.env.BSCSCAN_API_KEY || "", 22 | bscTestnet: process.env.BSCSCAN_API_KEY || "", 23 | // ETH 24 | goerli: process.env.ETHERSCAN_API_KEY || "", 25 | mainnet: process.env.ETHERSCAN_API_KEY || "", 26 | zeta_mainnet: "NO_TOKEN", 27 | zeta_testnet: "NO_TOKEN", 28 | }, 29 | //@ts-ignore 30 | customChains: [ 31 | { 32 | chainId: 7000, 33 | network: "zeta_mainnet", 34 | urls: { 35 | apiURL: "https://zetachain.blockscout.com/api", 36 | browserURL: "https://zetachain.blockscout.com", 37 | }, 38 | }, 39 | { 40 | chainId: 7001, 41 | network: "zeta_testnet", 42 | urls: { 43 | apiURL: "https://zetachain-athens-3.blockscout.com/api", 44 | browserURL: "https://zetachain-athens-3.blockscout.com", 45 | }, 46 | }, 47 | ], 48 | }, 49 | gasReporter: { 50 | currency: "USD", 51 | enabled: process.env.REPORT_GAS !== undefined, 52 | }, 53 | networks: { 54 | ...getHardhatConfigNetworks(), 55 | }, 56 | solidity: { 57 | compilers: [ 58 | { version: "0.5.10" /** For create2 factory */ }, 59 | { version: "0.6.6" /** For uniswap v2 */ }, 60 | { version: "0.8.7" }, 61 | { version: "0.4.18" /** For WETH / WZETA */ }, 62 | ], 63 | settings: { 64 | /** 65 | * @see {@link https://smock.readthedocs.io/en/latest/getting-started.html} 66 | */ 67 | outputSelection: { 68 | "*": { 69 | "*": ["storageLayout"], 70 | }, 71 | }, 72 | }, 73 | }, 74 | }; 75 | 76 | export default config; 77 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "zetachain", 3 | "license": "MIT", 4 | "version": "0.1.0", 5 | "workspaces": { 6 | "packages": [ 7 | "packages/*" 8 | ] 9 | }, 10 | "keywords": [ 11 | "zetachain", 12 | "blockchain", 13 | "solidity", 14 | "omnichain", 15 | "crosschain" 16 | ], 17 | "scripts": { 18 | "clean": "yarn workspaces foreach -pv run clean", 19 | "compile": "yarn workspaces foreach -pv run compile", 20 | "g:prepublishOnly": "yarn workspaces foreach -pv run prepublishOnly", 21 | "lint:fix": "npx eslint . --ext .js,.ts --fix", 22 | "lint": "npx eslint . --ext .js,.ts", 23 | "slither": "npx ts-node ./scripts/slither.ts", 24 | "test": "yarn workspaces foreach -ptv run test", 25 | "g:changeset": "changeset", 26 | "g:release": "yarn changeset publish", 27 | "setup-tutorial": "ts-node ./scripts/setup-tutorial.ts" 28 | }, 29 | "devDependencies": { 30 | "@changesets/cli": "^2.23.1", 31 | "@nomicfoundation/hardhat-verify": "2.0.3", 32 | "@nomiclabs/hardhat-ethers": "^2.2.3", 33 | "@nomiclabs/hardhat-waffle": "^2.0.3", 34 | "@typechain/ethers-v5": "^10.0.0", 35 | "@typechain/hardhat": "^6.0.0", 36 | "@types/chai": "^4.3.1", 37 | "@types/inquirer": "^8.2.1", 38 | "@types/mocha": "^10.0.1", 39 | "@types/node": "^17.0.25", 40 | "@typescript-eslint/eslint-plugin": "^5.20.0", 41 | "@typescript-eslint/parser": "^5.20.0", 42 | "@zetachain/toolkit": "10.0.0", 43 | "chai": "^4.3.6", 44 | "dotenv": "^16.0.0", 45 | "eslint": "^8.13.0", 46 | "eslint-config-prettier": "^8.5.0", 47 | "eslint-config-standard": "^17.0.0", 48 | "eslint-import-resolver-typescript": "^2.7.1", 49 | "eslint-plugin-import": "^2.26.0", 50 | "eslint-plugin-node": "^11.1.0", 51 | "eslint-plugin-prettier": "^4.0.0", 52 | "eslint-plugin-promise": "^6.0.0", 53 | "eslint-plugin-simple-import-sort": "7.0.0", 54 | "eslint-plugin-sort-keys-fix": "1.1.2", 55 | "eslint-plugin-typescript-sort-keys": "2.1.0", 56 | "ethereum-waffle": "^4.0.9", 57 | "ethereumjs-utils": "^5.2.5", 58 | "ethers": "5.7.2", 59 | "hardhat": "2.22.6", 60 | "inquirer": "^8.2.4", 61 | "mocha": "^10.2.0", 62 | "ts-mocha": "^10.0.0", 63 | "ts-node": "10.8.1", 64 | "typechain": "^8.0.0", 65 | "typescript": "^4.6.3" 66 | }, 67 | "packageManager": "yarn@3.2.0", 68 | "dependencies": { 69 | "solc": "0.8.7" 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /packages/zevm-app-contracts/contracts/disperse/Disperse.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.7; 3 | 4 | import "@openzeppelin/contracts/interfaces/IERC20.sol"; 5 | import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; 6 | 7 | contract Disperse { 8 | using SafeERC20 for IERC20; 9 | 10 | bool private locked; 11 | 12 | event FundsDispersed(address indexed token, address indexed from, address indexed recipient, uint256 value); 13 | 14 | modifier noReentrancy() { 15 | require(!locked, "No reentrancy"); 16 | locked = true; 17 | _; 18 | locked = false; 19 | } 20 | 21 | function disperseEther(address[] calldata recipients, uint256[] calldata values) external payable noReentrancy { 22 | require(recipients.length == values.length, "Recipients and values length mismatch"); 23 | 24 | for (uint256 i = 0; i < recipients.length; i++) { 25 | (bool sent, ) = payable(recipients[i]).call{value: values[i]}(""); 26 | require(sent, "Failed to send Ether"); 27 | emit FundsDispersed(address(0), msg.sender, recipients[i], values[i]); 28 | } 29 | 30 | uint256 balance = address(this).balance; 31 | if (balance > 0) { 32 | (bool sent, ) = payable(msg.sender).call{value: balance}(""); 33 | require(sent, "Failed to refund remaining Ether"); 34 | } 35 | } 36 | 37 | function disperseToken( 38 | IERC20 token, 39 | address[] calldata recipients, 40 | uint256[] calldata values 41 | ) external noReentrancy { 42 | uint256 total = 0; 43 | for (uint256 i = 0; i < recipients.length; i++) total += values[i]; 44 | token.safeTransferFrom(msg.sender, address(this), total); 45 | for (uint256 i = 0; i < recipients.length; i++) { 46 | token.safeTransfer(recipients[i], values[i]); 47 | emit FundsDispersed(address(token), msg.sender, recipients[i], values[i]); 48 | } 49 | } 50 | 51 | function disperseTokenSimple( 52 | IERC20 token, 53 | address[] calldata recipients, 54 | uint256[] calldata values 55 | ) external noReentrancy { 56 | for (uint256 i = 0; i < recipients.length; i++) { 57 | token.safeTransferFrom(msg.sender, recipients[i], values[i]); 58 | emit FundsDispersed(address(token), msg.sender, recipients[i], values[i]); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /packages/zevm-app-contracts/scripts/xp-nft/gov-set-level.ts: -------------------------------------------------------------------------------- 1 | import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; 2 | import { isProtocolNetworkName } from "@zetachain/protocol-contracts"; 3 | import { ethers, network } from "hardhat"; 4 | 5 | import { getSelLevelSignature } from "../../test/xp-nft/test.helpers"; 6 | import { ZetaXP_V2 } from "../../typechain-types"; 7 | import { getZEVMAppAddress, saveAddress } from "../address.helpers"; 8 | 9 | const networkName = network.name; 10 | 11 | const user = "0x19caCb4c0A7fC25598CC44564ED0eCA01249fc31"; 12 | const encodeTag = (tag: string) => ethers.utils.keccak256(ethers.utils.defaultAbiCoder.encode(["string"], [tag])); 13 | 14 | // Helper function to set the level of an NFT 15 | const setLevelToNFT = async (tokenId: number, level: number, zetaXP: ZetaXP_V2, signer: SignerWithAddress) => { 16 | const currentBlock = await ethers.provider.getBlock("latest"); 17 | const sigTimestamp = currentBlock.timestamp; 18 | const signatureExpiration = sigTimestamp + 1000; 19 | 20 | const currentChainId = (await ethers.provider.getNetwork()).chainId; 21 | const signature = await getSelLevelSignature( 22 | currentChainId, 23 | zetaXP.address, 24 | signer, 25 | signatureExpiration, 26 | sigTimestamp, 27 | tokenId, 28 | level 29 | ); 30 | 31 | await zetaXP.setLevel({ level, sigTimestamp, signature, signatureExpiration, tokenId }); 32 | }; 33 | 34 | const callGov = async () => { 35 | const [signer] = await ethers.getSigners(); 36 | 37 | if (!isProtocolNetworkName(networkName)) throw new Error("Invalid network name"); 38 | const zetaXPAddress = getZEVMAppAddress("ZetaXP", networkName); 39 | console.log("ZetaXP address:", zetaXPAddress); 40 | 41 | const XPNFT = await ethers.getContractFactory("ZetaXP_V2"); 42 | const xpnft = await XPNFT.attach(zetaXPAddress); 43 | 44 | const xpSigner = await xpnft.signerAddress(); 45 | console.log("XP Signer:", xpSigner); 46 | 47 | const tag = encodeTag("XP_NFT"); 48 | console.log("Tag:", tag); 49 | const id = await xpnft.tokenByUserTag(user, tag); 50 | console.log("ID:", id); 51 | 52 | const level = await xpnft.getLevel(id); 53 | console.log("Level:", level); 54 | 55 | if (level.toNumber() === 0) { 56 | await setLevelToNFT(id, 3, xpnft, signer); 57 | } 58 | }; 59 | 60 | const main = async () => { 61 | if (!isProtocolNetworkName(networkName)) throw new Error("Invalid network name"); 62 | await callGov(); 63 | }; 64 | 65 | main().catch((error) => { 66 | console.error(error); 67 | process.exit(1); 68 | }); 69 | -------------------------------------------------------------------------------- /packages/zevm-app-contracts/scripts/liquidity-incentives/deploy-default-rewards.ts: -------------------------------------------------------------------------------- 1 | import { BigNumber } from "@ethersproject/bignumber"; 2 | import { isProtocolNetworkName } from "@zetachain/protocol-contracts"; 3 | import { parseEther } from "ethers/lib/utils"; 4 | import { ethers, network } from "hardhat"; 5 | 6 | import { RewardDistributorFactory__factory, SystemContract__factory } from "../../typechain-types"; 7 | import { getSystemContractAddress, getZEVMAppAddress } from "../address.helpers"; 8 | import { addReward, deployRewardByNetwork } from "./helpers"; 9 | 10 | const REWARD_DURATION = BigNumber.from("604800").mul(8); // 1 week * 8 11 | const REWARDS_AMOUNT = parseEther("500"); 12 | 13 | const main = async () => { 14 | const [deployer] = await ethers.getSigners(); 15 | const networkName = network.name; 16 | 17 | if (!isProtocolNetworkName(networkName)) throw new Error("Invalid network name"); 18 | const systemContractAddress = getSystemContractAddress(); 19 | const systemContract = await SystemContract__factory.connect(systemContractAddress, deployer); 20 | 21 | const factoryContractAddress = getZEVMAppAddress("rewardDistributorFactory"); 22 | 23 | const rewardDistributorFactory = RewardDistributorFactory__factory.connect(factoryContractAddress, deployer); 24 | let rewardContractAddress = ""; 25 | // @dev: you can write your own address here to add reward to an existing contract 26 | // rewardContractAddress = "0x0dee8b6e2d2035a798b67c68d47f941718a62263"; 27 | rewardContractAddress = await deployRewardByNetwork( 28 | deployer, 29 | systemContract, 30 | "goerli_testnet", 31 | rewardDistributorFactory 32 | ); 33 | await addReward(deployer, systemContract, rewardContractAddress, REWARD_DURATION, REWARDS_AMOUNT); 34 | 35 | rewardContractAddress = await deployRewardByNetwork( 36 | deployer, 37 | systemContract, 38 | "bsc_testnet", 39 | rewardDistributorFactory 40 | ); 41 | await addReward(deployer, systemContract, rewardContractAddress, REWARD_DURATION, REWARDS_AMOUNT); 42 | rewardContractAddress = await deployRewardByNetwork( 43 | deployer, 44 | systemContract, 45 | "btc_testnet", 46 | rewardDistributorFactory 47 | ); 48 | await addReward(deployer, systemContract, rewardContractAddress, REWARD_DURATION, REWARDS_AMOUNT); 49 | rewardContractAddress = await deployRewardByNetwork( 50 | deployer, 51 | systemContract, 52 | "mumbai_testnet", 53 | rewardDistributorFactory 54 | ); 55 | await addReward(deployer, systemContract, rewardContractAddress, REWARD_DURATION, REWARDS_AMOUNT); 56 | }; 57 | 58 | main().catch((error) => { 59 | console.error(error); 60 | process.exit(1); 61 | }); 62 | -------------------------------------------------------------------------------- /packages/zevm-app-contracts/contracts/zeta-points/InvitationManagerV2.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.20; 3 | 4 | import "@openzeppelin/contracts/utils/cryptography/EIP712.sol"; 5 | import {SignatureChecker} from "@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol"; 6 | 7 | import "./InvitationManager.sol"; 8 | 9 | contract InvitationManagerV2 is EIP712 { 10 | bytes32 private constant VERIFY_TYPEHASH = keccak256("Verify(address to,uint256 signatureExpiration)"); 11 | 12 | InvitationManager public immutable invitationManager; 13 | 14 | struct VerifyData { 15 | address to; 16 | bytes signature; 17 | uint256 signatureExpiration; 18 | } 19 | 20 | // Records the timestamp when a particular user gets verified. 21 | mapping(address => uint256) public userVerificationTimestamps; 22 | 23 | error UserAlreadyVerified(); 24 | error SignatureExpired(); 25 | error InvalidSigner(); 26 | 27 | event UserVerified(address indexed userAddress, uint256 verifiedAt, uint256 unix_timestamp); 28 | 29 | constructor(InvitationManager _invitationManager) EIP712("InvitationManagerV2", "1") { 30 | invitationManager = _invitationManager; 31 | } 32 | 33 | function _markAsVerified(address user) internal { 34 | // Check if the user is already verified 35 | if (hasBeenVerified(user)) revert UserAlreadyVerified(); 36 | 37 | userVerificationTimestamps[user] = block.timestamp; 38 | emit UserVerified(user, block.timestamp, block.timestamp); 39 | } 40 | 41 | function markAsVerified() external { 42 | _markAsVerified(msg.sender); 43 | } 44 | 45 | function hasBeenVerified(address userAddress) public view returns (bool) { 46 | if (userVerificationTimestamps[userAddress] > 0) return true; 47 | if (address(invitationManager) != address(0) && invitationManager.hasBeenVerified(userAddress)) return true; 48 | return false; 49 | } 50 | 51 | function _verify(VerifyData memory claimData) private view { 52 | bytes32 structHash = keccak256(abi.encode(VERIFY_TYPEHASH, claimData.to, claimData.signatureExpiration)); 53 | bytes32 constructedHash = _hashTypedDataV4(structHash); 54 | 55 | if (!SignatureChecker.isValidSignatureNow(claimData.to, constructedHash, claimData.signature)) { 56 | revert InvalidSigner(); 57 | } 58 | 59 | if (block.timestamp > claimData.signatureExpiration) revert SignatureExpired(); 60 | } 61 | 62 | function markAsVerifiedWithSignature(VerifyData memory data) external { 63 | _verify(data); 64 | _markAsVerified(data.to); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /packages/zevm-app-contracts/scripts/xp-nft/deploy-gov.ts: -------------------------------------------------------------------------------- 1 | import { isProtocolNetworkName } from "@zetachain/protocol-contracts"; 2 | import { ethers, network } from "hardhat"; 3 | 4 | import { getZEVMAppAddress, saveAddress } from "../address.helpers"; 5 | import { verifyContract } from "../explorer.helpers"; 6 | 7 | const QUANTUM_PERCENTAGE = 4; 8 | 9 | const networkName = network.name; 10 | 11 | const encodeTag = (tag: string) => ethers.utils.keccak256(ethers.utils.defaultAbiCoder.encode(["string"], [tag])); 12 | 13 | const executeVerifyContract = async (name: string, address: string, args: any[]) => { 14 | if (!isProtocolNetworkName(networkName)) throw new Error("Invalid network name"); 15 | 16 | console.log(`${name} deployed to:`, address); 17 | saveAddress(`${name}`, address, networkName); 18 | await verifyContract(address, args); 19 | }; 20 | 21 | const deployXPGov = async () => { 22 | const [signer] = await ethers.getSigners(); 23 | 24 | if (!isProtocolNetworkName(networkName)) throw new Error("Invalid network name"); 25 | const zetaXPAddress = getZEVMAppAddress("ZetaXP", networkName); 26 | console.log("ZetaXP address:", zetaXPAddress); 27 | 28 | // Deploy the TimelockController contract 29 | const timelockFactory = await ethers.getContractFactory("TimelockController"); 30 | const timelock = await timelockFactory.deploy(3600, [signer.address], [signer.address], signer.address); 31 | await timelock.deployed(); 32 | 33 | const timelockAddress = timelock.address; 34 | executeVerifyContract("TimelockController", timelockAddress, [ 35 | 3600, 36 | [signer.address], 37 | [signer.address], 38 | signer.address, 39 | ]); 40 | 41 | const tag = encodeTag("XP_NFT"); 42 | 43 | // Deploy the ZetaXPGov contract 44 | const ZetaXPGovFactory = await ethers.getContractFactory("ZetaXPGov"); 45 | const zetaGov = await ZetaXPGovFactory.deploy(zetaXPAddress, timelockAddress, QUANTUM_PERCENTAGE, tag); 46 | await zetaGov.deployed(); 47 | 48 | const zetaGovAddress = zetaGov.address; 49 | 50 | executeVerifyContract("ZetaXPGov", zetaGovAddress, [zetaXPAddress, timelockAddress, QUANTUM_PERCENTAGE, tag]); 51 | 52 | // Assign proposer and executor roles to the signer 53 | const proposerRole = await timelock.PROPOSER_ROLE(); 54 | const executorRole = await timelock.EXECUTOR_ROLE(); 55 | await timelock.grantRole(proposerRole, zetaGovAddress); 56 | await timelock.grantRole(executorRole, zetaGovAddress); 57 | }; 58 | 59 | const main = async () => { 60 | if (!isProtocolNetworkName(networkName)) throw new Error("Invalid network name"); 61 | await deployXPGov(); 62 | }; 63 | 64 | main().catch((error) => { 65 | console.error(error); 66 | process.exit(1); 67 | }); 68 | -------------------------------------------------------------------------------- /packages/zevm-app-contracts/hardhat.config.ts: -------------------------------------------------------------------------------- 1 | import "@nomicfoundation/hardhat-verify"; 2 | import "@nomiclabs/hardhat-waffle"; 3 | import "@typechain/hardhat"; 4 | import "@openzeppelin/hardhat-upgrades"; 5 | import "hardhat-gas-reporter"; 6 | import "solidity-coverage"; 7 | import "tsconfig-paths/register"; 8 | 9 | import { getHardhatConfigNetworks } from "@zetachain/networks"; 10 | import * as dotenv from "dotenv"; 11 | import type { HardhatUserConfig } from "hardhat/types"; 12 | 13 | dotenv.config(); 14 | 15 | const PRIVATE_KEYS = process.env.PRIVATE_KEY !== undefined ? [`0x${process.env.PRIVATE_KEY}`] : []; 16 | 17 | const config: HardhatUserConfig = { 18 | //@ts-ignore 19 | etherscan: { 20 | apiKey: { 21 | // BSC 22 | bscTestnet: process.env.BSCSCAN_API_KEY || "", 23 | // ETH 24 | goerli: process.env.ETHERSCAN_API_KEY || "", 25 | mainnet: process.env.ETHERSCAN_API_KEY || "", 26 | zeta_mainnet: "NO_TOKEN", 27 | zeta_testnet: "NO_TOKEN", 28 | }, 29 | //@ts-ignore 30 | customChains: [ 31 | { 32 | chainId: 7000, 33 | network: "zeta_mainnet", 34 | urls: { 35 | apiURL: "https://zetachain.blockscout.com/api", 36 | browserURL: "https://zetachain.blockscout.com", 37 | }, 38 | }, 39 | { 40 | chainId: 7001, 41 | network: "zeta_testnet", 42 | urls: { 43 | apiURL: "https://zetachain-testnet.blockscout.com/api", 44 | browserURL: "https://zetachain-testnet.blockscout.com", 45 | }, 46 | }, 47 | ], 48 | }, 49 | gasReporter: { 50 | currency: "USD", 51 | enabled: process.env.REPORT_GAS !== undefined, 52 | }, 53 | networks: { 54 | ...getHardhatConfigNetworks(), 55 | zeta_mainnet: { 56 | accounts: PRIVATE_KEYS, 57 | chainId: 7000, 58 | gas: "auto", 59 | gasMultiplier: 3, 60 | url: `https://zetachain-evm.blockpi.network/v1/rpc/public`, 61 | }, 62 | }, 63 | solidity: { 64 | compilers: [ 65 | { version: "0.5.10" /** For create2 factory */ }, 66 | { version: "0.6.6" /** For uniswap v2 */ }, 67 | { version: "0.8.7" }, 68 | { version: "0.8.9" }, 69 | { 70 | settings: { 71 | optimizer: { 72 | enabled: true, 73 | runs: 1000, 74 | }, 75 | }, 76 | version: "0.8.20", 77 | }, 78 | ], 79 | settings: { 80 | /** 81 | * @see {@link https://smock.readthedocs.io/en/latest/getting-started.html} 82 | */ 83 | outputSelection: { 84 | "*": { 85 | "*": ["storageLayout"], 86 | }, 87 | }, 88 | }, 89 | }, 90 | }; 91 | 92 | export default config; 93 | -------------------------------------------------------------------------------- /packages/zeta-app-contracts/lib/shared/ImmutableCreate2Factory.helpers.ts: -------------------------------------------------------------------------------- 1 | import { Provider, TransactionReceipt } from "@ethersproject/providers"; 2 | import { ImmutableCreate2Factory__factory } from "@zetachain/protocol-contracts/dist/typechain-types"; 3 | import { ethers, Signer } from "ethers"; 4 | 5 | export const buildBytecode = (constructorTypes: any[], constructorArgs: any[], contractBytecode: string) => 6 | `${contractBytecode}${encodeParams(constructorTypes, constructorArgs).slice(2)}`; 7 | 8 | export const buildCreate2Address = (saltHex: string, byteCode: string, factoryAddress: string) => { 9 | const payload = ethers.utils.keccak256( 10 | `0x${["ff", factoryAddress, saltHex, ethers.utils.keccak256(byteCode)].map((x) => x.replace(/0x/, "")).join("")}` 11 | ); 12 | 13 | return `0x${payload.slice(-40)}`.toLowerCase(); 14 | }; 15 | 16 | export const numberToUint256 = (value: number) => { 17 | const hex = value.toString(16); 18 | return `0x${"0".repeat(64 - hex.length)}${hex}`; 19 | }; 20 | 21 | export const saltToHex = (salt: string, signerAddress: string) => { 22 | let salthex = ethers.utils.formatBytes32String(salt); 23 | return `${signerAddress}${salthex.slice(2)}`.substring(0, salthex.length); 24 | }; 25 | 26 | export const encodeParam = (dataType: any, data: any) => { 27 | const abiCoder = ethers.utils.defaultAbiCoder; 28 | return abiCoder.encode([dataType], [data]); 29 | }; 30 | 31 | export const encodeParams = (dataTypes: any[], data: any[]) => { 32 | const abiCoder = ethers.utils.defaultAbiCoder; 33 | return abiCoder.encode(dataTypes, data); 34 | }; 35 | 36 | export async function deployContractToAddress({ 37 | factoryAddress, 38 | salt, 39 | contractBytecode, 40 | constructorTypes = [] as string[], 41 | constructorArgs = [] as any[], 42 | signer, 43 | }: { 44 | constructorArgs?: any[]; 45 | constructorTypes?: string[]; 46 | contractBytecode: string; 47 | factoryAddress: string; 48 | salt: string; 49 | signer: Signer; 50 | }) { 51 | const factory = ImmutableCreate2Factory__factory.connect(factoryAddress, signer); 52 | 53 | const bytecode = buildBytecode(constructorTypes, constructorArgs, contractBytecode); 54 | 55 | const computedAddr = await factory.findCreate2Address(salt, bytecode); 56 | 57 | const tx = await factory.safeCreate2(salt, bytecode, { 58 | gasLimit: 6000000, 59 | }); 60 | const result = await tx.wait(); 61 | 62 | return { 63 | address: computedAddr as string, 64 | receipt: result as TransactionReceipt, 65 | txHash: result.transactionHash as string, 66 | }; 67 | } 68 | 69 | /** 70 | * Determines if there's a contract deployed on the provided address. 71 | */ 72 | export async function isDeployed(address: string, provider: Provider) { 73 | const code = await provider.getCode(address); 74 | return code.slice(2).length > 0; 75 | } 76 | -------------------------------------------------------------------------------- /scripts/slither.ts: -------------------------------------------------------------------------------- 1 | import inquirer from "inquirer"; 2 | import { execSync } from "node:child_process"; 3 | import path from "node:path"; 4 | 5 | const projectRoot = path.join(__dirname, "../"); 6 | const solcVersion = "0.8.7"; 7 | const timestamp = Date.now(); 8 | const packageNames = ["protocol-contracts", "example-contracts"]; 9 | 10 | async function getPackageName() { 11 | let packageName; 12 | 13 | if (process.env.CI) { 14 | packageName = process.argv[2]; 15 | 16 | if (!packageNames.includes(packageName)) { 17 | console.error(`${packageName} is not a valid package name.`); 18 | console.error(`Valid package names are: ${packageNames.join(", ")}`); 19 | process.exit(1); 20 | } 21 | 22 | return packageName; 23 | } else { 24 | packageName = await inquirer.prompt([ 25 | { 26 | choices: packageNames, 27 | message: "Which set of contracts would you like to test?", 28 | name: "contracts", 29 | type: "list", 30 | }, 31 | ]); 32 | 33 | return packageName.contracts; 34 | } 35 | } 36 | 37 | async function getFilterPaths() { 38 | if (process.env.CI) return ""; 39 | 40 | const { confirm: includeLibraries } = await inquirer.prompt([ 41 | { 42 | message: "Do you want to include OpenZeppelin & Uniswap libraries in this scan?", 43 | name: "confirm", 44 | type: "confirm", 45 | }, 46 | ]); 47 | 48 | return includeLibraries ? "" : `--filter-paths "node_modules/@openzeppelin/","node_modules/@uniswap/"`; 49 | } 50 | 51 | const run = async (command: string) => { 52 | try { 53 | console.log("Starting -- This may take a few minutes..."); 54 | 55 | execSync(command, { 56 | encoding: "utf-8", 57 | stdio: "inherit", 58 | }); 59 | 60 | console.log("Results output to the console and saved to slither-output/ in Markdown, JSON, and SARIF formats."); 61 | } catch (error) { 62 | console.error("Error: Docker Failed To Run"); 63 | console.error(`${error}`); 64 | } 65 | }; 66 | function runSlither(packageName: string, filterPaths: string) { 67 | const dockerCommand = `cd /home/trufflecon/packages/${packageName} && \ 68 | solc-select use ${solcVersion} && \ 69 | slither --json ../../scripts/slither-results/${packageName}-${timestamp}.json \ 70 | --sarif ../../scripts/slither-results/${packageName}-${timestamp}.sarif \ 71 | --checklist ./ ${filterPaths} | tee ../../scripts/slither-results/${packageName}-${timestamp}.md`; 72 | run(`docker run -v "${projectRoot}":/home/trufflecon trailofbits/eth-security-toolbox -c "${dockerCommand}"`); 73 | } 74 | 75 | async function main() { 76 | runSlither(await getPackageName(), await getFilterPaths()); 77 | } 78 | 79 | main() 80 | .then(() => process.exit(0)) 81 | .catch((error) => { 82 | console.error(error); 83 | process.exit(1); 84 | }); 85 | -------------------------------------------------------------------------------- /packages/zevm-app-contracts/contracts/zeta-swap/ZetaCurveSwapDemo.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.7; 3 | 4 | import "@zetachain/protocol-contracts/contracts/zevm/interfaces/zContract.sol"; 5 | import "@zetachain/protocol-contracts/contracts/zevm/interfaces/IZRC20.sol"; 6 | 7 | interface ICRV3 { 8 | function exchange(uint256 i, uint256 j, uint256 dx, uint256 min_dy, bool use_eth) external returns (uint256); 9 | } 10 | 11 | interface ZetaCurveSwapErrors { 12 | error WrongGasContract(); 13 | 14 | error NotEnoughToPayGasFee(); 15 | 16 | error InvalidAddress(); 17 | } 18 | 19 | contract ZetaCurveSwapDemo is zContract, ZetaCurveSwapErrors { 20 | address public crv3pool; // gETH/tBNB/tMATIC pool 21 | address[3] public crvZRC20s; 22 | 23 | constructor(address crv3pool_, address[3] memory ZRC20s_) { 24 | if (crv3pool_ == address(0) || ZRC20s_[0] == address(0) || ZRC20s_[1] == address(0) || ZRC20s_[2] == address(0)) 25 | revert InvalidAddress(); 26 | crv3pool = crv3pool_; 27 | crvZRC20s = ZRC20s_; 28 | } 29 | 30 | function encode(address zrc20, address recipient, uint256 minAmountOut) public pure returns (bytes memory) { 31 | return abi.encode(zrc20, recipient, minAmountOut); 32 | } 33 | 34 | function addr2idx(address zrc20) public view returns (uint256) { 35 | for (uint256 i = 0; i < 3; i++) { 36 | if (crvZRC20s[i] == zrc20) { 37 | return i; 38 | } 39 | } 40 | return 18; 41 | } 42 | 43 | function _doWithdrawal(address targetZRC20, uint256 amount, bytes32 receipient) private { 44 | (address gasZRC20, uint256 gasFee) = IZRC20(targetZRC20).withdrawGasFee(); 45 | 46 | if (gasZRC20 != targetZRC20) revert WrongGasContract(); 47 | if (gasFee >= amount) revert NotEnoughToPayGasFee(); 48 | 49 | IZRC20(targetZRC20).approve(targetZRC20, gasFee); 50 | IZRC20(targetZRC20).withdraw(abi.encodePacked(receipient), amount - gasFee); 51 | } 52 | 53 | function onCrossChainCall( 54 | zContext calldata context, 55 | address zrc20, 56 | uint256 amount, 57 | bytes calldata message 58 | ) external override { 59 | (address targetZRC20, bytes32 receipient, ) = abi.decode(message, (address, bytes32, uint256)); 60 | 61 | address[] memory path = new address[](2); 62 | path[0] = zrc20; 63 | path[1] = targetZRC20; 64 | IZRC20(zrc20).approve(address(crv3pool), amount); 65 | 66 | uint256 i = addr2idx(zrc20); 67 | uint256 j = addr2idx(targetZRC20); 68 | require(i >= 0 && i < 3 && j >= 0 && j < 3 && i != j, "i,j error"); 69 | 70 | uint256 outAmount = ICRV3(crv3pool).exchange(i, j, amount, 0, false); 71 | 72 | _doWithdrawal(targetZRC20, outAmount, receipient); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /packages/zevm-app-contracts/contracts/liquidity-incentives/RewardDistributorFactory.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.7; 3 | 4 | import "@openzeppelin/contracts/interfaces/IERC20.sol"; 5 | import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; 6 | 7 | import "@zetachain/protocol-contracts/contracts/zevm/SystemContract.sol"; 8 | 9 | import "./RewardDistributor.sol"; 10 | 11 | contract RewardDistributorFactory { 12 | address public immutable zetaTokenAddress; 13 | SystemContract private immutable _systemContract; 14 | 15 | mapping(uint256 => address) public incentivesContracts; 16 | uint256 public incentivesContractsLen; 17 | 18 | event RewardDistributorCreated( 19 | address rewardDistributorContract, 20 | address stakingTokenA, 21 | address stakingTokenB, 22 | address LPStakingToken, 23 | address rewardsToken, 24 | address owner 25 | ); 26 | 27 | constructor(address _zetaTokenAddress, address systemContract) { 28 | zetaTokenAddress = _zetaTokenAddress; 29 | _systemContract = SystemContract(systemContract); 30 | } 31 | 32 | function createTokenIncentive( 33 | address owner, 34 | ///@dev _rewardsDistribution is one who can set the amount of token to reward 35 | address rewardsDistribution, 36 | address rewardsToken, 37 | address stakingTokenA, 38 | address stakingTokenB 39 | ) external { 40 | if (stakingTokenB == address(0)) { 41 | stakingTokenB = zetaTokenAddress; 42 | } 43 | if (rewardsToken == address(0)) { 44 | rewardsToken = zetaTokenAddress; 45 | } 46 | address LPTokenAddress = _systemContract.uniswapv2PairFor( 47 | _systemContract.uniswapv2FactoryAddress(), 48 | stakingTokenA, 49 | stakingTokenB 50 | ); 51 | RewardDistributor incentiveContract = new RewardDistributor( 52 | owner, 53 | rewardsDistribution, 54 | rewardsToken, 55 | LPTokenAddress, 56 | stakingTokenA, 57 | stakingTokenB, 58 | address(_systemContract) 59 | ); 60 | incentivesContracts[incentivesContractsLen++] = address(incentiveContract); 61 | 62 | emit RewardDistributorCreated( 63 | address(incentiveContract), 64 | stakingTokenA, 65 | stakingTokenB, 66 | LPTokenAddress, 67 | rewardsToken, 68 | owner 69 | ); 70 | } 71 | 72 | function getIncentiveContracts() public view returns (address[] memory) { 73 | address[] memory result = new address[](incentivesContractsLen); 74 | for (uint256 i = 0; i < incentivesContractsLen; i++) { 75 | result[i] = incentivesContracts[i]; 76 | } 77 | return result; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /packages/zeta-app-contracts/lib/multi-chain-value/MultiChainValue.helpers.ts: -------------------------------------------------------------------------------- 1 | import { getAddress, isProtocolNetworkName } from "@zetachain/protocol-contracts"; 2 | import { ZetaEth, ZetaEth__factory as ZetaEthFactory } from "@zetachain/protocol-contracts/dist/typechain-types"; 3 | import assert from "assert"; 4 | import { ethers, network } from "hardhat"; 5 | 6 | import { getContract } from "../../lib/shared/deploy.helpers"; 7 | import { 8 | MultiChainValue, 9 | MultiChainValue__factory as MultiChainValueFactory, 10 | MultiChainValueMock, 11 | MultiChainValueMock__factory as MultiChainValueMockFactory, 12 | ZetaConnectorMockValue, 13 | ZetaConnectorMockValue__factory as ZetaConnectorMockValueFactory, 14 | } from "../../typechain-types"; 15 | 16 | const networkName = network.name; 17 | /** 18 | * @description only for testing or local environment 19 | */ 20 | export const deployMultiChainValueMock = async ({ 21 | zetaConnectorMockAddress, 22 | zetaTokenMockAddress, 23 | }: { 24 | zetaConnectorMockAddress: string; 25 | zetaTokenMockAddress: string; 26 | }) => { 27 | const isLocalEnvironment = network.name === "hardhat"; 28 | 29 | assert(isLocalEnvironment, "This function is only intended to be used in the local environment"); 30 | 31 | const Factory = (await ethers.getContractFactory("MultiChainValueMock")) as MultiChainValueMockFactory; 32 | 33 | const multiChainValueContract = (await Factory.deploy( 34 | zetaConnectorMockAddress, 35 | zetaTokenMockAddress 36 | )) as MultiChainValueMock; 37 | 38 | await multiChainValueContract.deployed(); 39 | 40 | return multiChainValueContract; 41 | }; 42 | 43 | export const getMultiChainValue = (existingContractAddress?: string) => { 44 | if (!isProtocolNetworkName(networkName)) throw new Error("Invalid network name"); 45 | 46 | return getContract({ 47 | contractName: "MultiChainValue", 48 | ...(existingContractAddress 49 | ? { existingContractAddress } 50 | : { deployParams: [getAddress("connector", networkName), getAddress("zetaToken", networkName)] }), 51 | }); 52 | }; 53 | 54 | export const deployZetaConnectorMock = async () => { 55 | const Factory = (await ethers.getContractFactory("ZetaConnectorMockValue")) as ZetaConnectorMockValueFactory; 56 | 57 | const zetaConnectorMockContract = (await Factory.deploy()) as ZetaConnectorMockValue; 58 | 59 | await zetaConnectorMockContract.deployed(); 60 | 61 | return zetaConnectorMockContract; 62 | }; 63 | 64 | export const deployZetaEthMock = async () => { 65 | const [signer] = await ethers.getSigners(); 66 | 67 | const Factory = (await ethers.getContractFactory("ZetaEthMock")) as ZetaEthFactory; 68 | 69 | const zetaConnectorMockContract = (await Factory.deploy(signer.address, 100_000)) as ZetaEth; 70 | 71 | await zetaConnectorMockContract.deployed(); 72 | 73 | return zetaConnectorMockContract; 74 | }; 75 | -------------------------------------------------------------------------------- /packages/zeta-app-contracts/scripts/multi-chain-value/search-reverted-tx.ts: -------------------------------------------------------------------------------- 1 | import { ContractReceipt, ContractTransaction, Transaction } from "ethers"; 2 | import { ethers, network } from "hardhat"; 3 | 4 | // Specify the address you're interested in 5 | const ADDRESS = "0x70e967acFcC17c3941E87562161406d41676FD83"; 6 | let API_KEY = ""; 7 | let API_ENDPOINT = ""; 8 | let START_BLOCK = 0; 9 | let END_BLOCK = 0; 10 | 11 | if (network.name === "bsc_mainnet") { 12 | API_KEY = process.env.BSCSCAN_API_KEY || ""; 13 | API_ENDPOINT = "api.bscscan.com"; 14 | START_BLOCK = 35741686; 15 | END_BLOCK = 35844713; 16 | } else if (network.name === "eth_mainnet") { 17 | API_KEY = process.env.ETHERSCAN_API_KEY || ""; 18 | API_ENDPOINT = "api.etherscan.io"; 19 | START_BLOCK = 19080591; 20 | END_BLOCK = 19157698; 21 | } else { 22 | throw new Error("Unsupported network"); 23 | } 24 | 25 | // Function to check if a transaction involves the specified address 26 | const isTransactionOfInterest = (tx: any, address: string) => { 27 | return tx.from.toLowerCase() === address.toLowerCase() || tx.to.toLowerCase() === address.toLowerCase(); 28 | }; 29 | 30 | // Main function to iterate over the block range and find transactions 31 | const findTransactionsInRange = async () => { 32 | let totalTx = 0; 33 | let totalErrorTx = 0; 34 | for (let i = START_BLOCK; i < END_BLOCK; i++) { 35 | // console.log(`Fetching block ${i} to ${END_BLOCK}...`); 36 | const API_URL = `https://${API_ENDPOINT}/api?module=account&action=txlist&address=${ADDRESS}&startblock=${i}&endblock=${END_BLOCK}&sort=asc&apikey=${API_KEY}`; 37 | 38 | try { 39 | const call = await fetch(API_URL); 40 | const response = await call.json(); 41 | const result = response.result; 42 | const firstBlock = result[0].blockNumber; 43 | const lastBlock = result[result.length - 1].blockNumber; 44 | console.log(`Fetched block ${firstBlock} to ${lastBlock}...`); 45 | 46 | const filteredTx = result.filter((tx: any) => isTransactionOfInterest(tx, ADDRESS)); 47 | const nonSuccesssfulTx = filteredTx.filter((tx: any) => tx.isError === "1" || tx.txreceipt_status !== "1"); 48 | 49 | totalTx += filteredTx.length; 50 | totalErrorTx += nonSuccesssfulTx.length; 51 | for (const tx of nonSuccesssfulTx) { 52 | console.log(tx.hash); 53 | } 54 | if (result.length > 9000) { 55 | i = parseInt(result[result.length - 1].blockNumber) - 1; 56 | } 57 | if (lastBlock >= END_BLOCK) { 58 | break; 59 | } 60 | } catch (e) { 61 | console.log(`Error fetching block ${i}`, e); 62 | } 63 | } 64 | console.log(`total tx: ${totalTx} / Errors: ${totalErrorTx}`); 65 | }; 66 | 67 | // Call the main function 68 | const main = async () => { 69 | await findTransactionsInRange(); 70 | }; 71 | 72 | main().catch((error) => { 73 | console.error(error); 74 | process.exit(1); 75 | }); 76 | -------------------------------------------------------------------------------- /packages/zevm-app-contracts/scripts/liquidity-incentives/read-rewards-data.ts: -------------------------------------------------------------------------------- 1 | import { isProtocolNetworkName } from "@zetachain/protocol-contracts"; 2 | import { ethers, network } from "hardhat"; 3 | 4 | import { RewardDistributor__factory, RewardDistributorFactory__factory } from "../../typechain-types"; 5 | import { getZEVMAppAddress } from "../address.helpers"; 6 | 7 | const networkName = network.name; 8 | 9 | const readRewardData = async (rewardContractAddress: string) => { 10 | const [deployer] = await ethers.getSigners(); 11 | const rewardDistributorContract = await RewardDistributor__factory.connect(rewardContractAddress, deployer); 12 | 13 | const stakingTokenA = await rewardDistributorContract.stakingTokenA(); 14 | const stakingTokenB = await rewardDistributorContract.stakingTokenB(); 15 | 16 | const rewardsToken = await rewardDistributorContract.rewardsToken(); 17 | const stakingToken = await rewardDistributorContract.stakingToken(); 18 | const periodFinish = await rewardDistributorContract.periodFinish(); 19 | const rewardRate = await rewardDistributorContract.rewardRate(); 20 | const rewardsDuration = await rewardDistributorContract.rewardsDuration(); 21 | const lastUpdateTime = await rewardDistributorContract.lastUpdateTime(); 22 | const rewardPerTokenStored = await rewardDistributorContract.rewardPerTokenStored(); 23 | if (rewardRate.isZero()) return; 24 | console.table({ 25 | contract: rewardContractAddress, 26 | lastUpdateTime: `${lastUpdateTime.toString()}-${new Date(lastUpdateTime.toNumber() * 1000).toISOString()}`, 27 | periodFinish: `${periodFinish.toString()}-${new Date(periodFinish.toNumber() * 1000).toISOString()}`, 28 | rewardPerTokenStored: rewardPerTokenStored.toString(), 29 | rewardRate: rewardRate.toString(), 30 | rewardsDuration: rewardsDuration.toString(), 31 | rewardsToken: rewardsToken, 32 | stakingToken: stakingToken, 33 | stakingTokenA, 34 | stakingTokenB, 35 | }); 36 | }; 37 | 38 | async function main() { 39 | const [deployer] = await ethers.getSigners(); 40 | if (!isProtocolNetworkName(networkName)) throw new Error("Invalid network name"); 41 | 42 | const factoryContractAddress = getZEVMAppAddress("rewardDistributorFactory"); 43 | 44 | const rewardDistributorFactory = RewardDistributorFactory__factory.connect(factoryContractAddress, deployer); 45 | const incentivesContractsLen = await rewardDistributorFactory.incentivesContractsLen(); 46 | 47 | const incentiveContracts: string[] = []; 48 | for (let i = 0; i < incentivesContractsLen.toNumber(); i++) { 49 | const incentiveContract = await rewardDistributorFactory.incentivesContracts(i); 50 | incentiveContracts.push(incentiveContract); 51 | } 52 | 53 | console.log("incentiveContracts", incentiveContracts); 54 | incentiveContracts.forEach(async (incentiveContract) => { 55 | await readRewardData(incentiveContract); 56 | }); 57 | } 58 | 59 | main().catch((error) => { 60 | console.error(error); 61 | process.exit(1); 62 | }); 63 | -------------------------------------------------------------------------------- /packages/zevm-app-contracts/test/test.helpers.ts: -------------------------------------------------------------------------------- 1 | import { MaxUint256 } from "@ethersproject/constants"; 2 | import { parseEther, parseUnits } from "@ethersproject/units"; 3 | import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; 4 | import { ethers } from "hardhat"; 5 | 6 | import { 7 | MockSystemContract, 8 | MockSystemContract__factory, 9 | MockZRC20, 10 | MockZRC20__factory, 11 | UniswapV2Router02__factory, 12 | } from "../typechain-types"; 13 | 14 | const addZetaEthLiquidity = async (signer: SignerWithAddress, token: MockZRC20, uniswapRouterAddr: string) => { 15 | const block = await ethers.provider.getBlock("latest"); 16 | 17 | const tx1 = await token.approve(uniswapRouterAddr, MaxUint256); 18 | await tx1.wait(); 19 | 20 | const uniswapRouterFork = UniswapV2Router02__factory.connect(uniswapRouterAddr, signer); 21 | 22 | const tx2 = await uniswapRouterFork.addLiquidityETH( 23 | token.address, 24 | parseUnits("2000"), 25 | 0, 26 | 0, 27 | signer.address, 28 | block.timestamp + 360, 29 | { value: parseUnits("1000") } 30 | ); 31 | await tx2.wait(); 32 | }; 33 | 34 | interface EvmSetupResult { 35 | ZRC20Contracts: MockZRC20[]; 36 | systemContract: MockSystemContract; 37 | } 38 | 39 | export const evmSetup = async ( 40 | wGasToken: string, 41 | uniswapFactoryAddr: string, 42 | uniswapRouterAddr: string 43 | ): Promise => { 44 | const [signer] = await ethers.getSigners(); 45 | 46 | const ZRC20Factory = (await ethers.getContractFactory("MockZRC20")) as MockZRC20__factory; 47 | 48 | const token1Contract = (await ZRC20Factory.deploy(parseUnits("1000000"), "tBNB", "tBNB")) as MockZRC20; 49 | const token2Contract = (await ZRC20Factory.deploy(parseUnits("1000000"), "gETH", "gETH")) as MockZRC20; 50 | const token3Contract = (await ZRC20Factory.deploy(parseUnits("1000000"), "tMATIC", "tMATIC")) as MockZRC20; 51 | const token4Contract = (await ZRC20Factory.deploy(parseUnits("1000000"), "USDC", "USDC")) as MockZRC20; 52 | 53 | const ZRC20Contracts = [token1Contract, token2Contract, token3Contract, token4Contract]; 54 | 55 | const SystemContractFactory = (await ethers.getContractFactory("MockSystemContract")) as MockSystemContract__factory; 56 | 57 | const systemContract = (await SystemContractFactory.deploy( 58 | wGasToken, 59 | uniswapFactoryAddr, 60 | uniswapRouterAddr 61 | )) as MockSystemContract; 62 | 63 | await systemContract.setGasCoinZRC20(97, ZRC20Contracts[0].address); 64 | await systemContract.setGasCoinZRC20(5, ZRC20Contracts[1].address); 65 | await systemContract.setGasCoinZRC20(80001, ZRC20Contracts[2].address); 66 | await ZRC20Contracts[3].setGasFeeAddress(ZRC20Contracts[1].address); 67 | await ZRC20Contracts[3].setGasFee(parseEther("0.01")); 68 | 69 | await addZetaEthLiquidity(signer, ZRC20Contracts[0], uniswapRouterAddr); 70 | await addZetaEthLiquidity(signer, ZRC20Contracts[1], uniswapRouterAddr); 71 | await addZetaEthLiquidity(signer, ZRC20Contracts[2], uniswapRouterAddr); 72 | await addZetaEthLiquidity(signer, ZRC20Contracts[3], uniswapRouterAddr); 73 | 74 | return { ZRC20Contracts, systemContract }; 75 | }; 76 | -------------------------------------------------------------------------------- /packages/zevm-app-contracts/contracts/multi-output/ZetaMultiOutput.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.7; 3 | 4 | import "@openzeppelin/contracts/access/Ownable.sol"; 5 | import "@zetachain/protocol-contracts/contracts/zevm/SystemContract.sol"; 6 | import "../shared/BytesHelperLib.sol"; 7 | import "../shared/SwapHelperLib.sol"; 8 | 9 | interface ZetaMultiOutputErrors { 10 | error NoAvailableTransfers(); 11 | } 12 | 13 | contract ZetaMultiOutput is zContract, Ownable, ZetaMultiOutputErrors { 14 | SystemContract public immutable systemContract; 15 | address[] public destinationTokens; 16 | 17 | event DestinationRegistered(address); 18 | event Withdrawal(address, uint256, address); 19 | 20 | constructor(address systemContractAddress) { 21 | systemContract = SystemContract(systemContractAddress); 22 | } 23 | 24 | modifier onlySystem() { 25 | require(msg.sender == address(systemContract), "Only system contract can call this function"); 26 | _; 27 | } 28 | 29 | function registerDestinationToken(address destinationToken) external onlyOwner { 30 | destinationTokens.push(destinationToken); 31 | emit DestinationRegistered(destinationToken); 32 | } 33 | 34 | function _getTotalTransfers(address zrc20) internal view returns (uint256) { 35 | uint256 total = 0; 36 | for (uint256 i; i < destinationTokens.length; i++) { 37 | if (destinationTokens[i] == zrc20) continue; 38 | total++; 39 | } 40 | 41 | return total; 42 | } 43 | 44 | function onCrossChainCall( 45 | zContext calldata context, 46 | address zrc20, 47 | uint256 amount, 48 | bytes calldata message 49 | ) external virtual override onlySystem { 50 | if (_getTotalTransfers(zrc20) == 0) revert NoAvailableTransfers(); 51 | 52 | address receipient = BytesHelperLib.bytesToAddress(message, 0); 53 | uint256 amountToTransfer = amount / _getTotalTransfers(zrc20); 54 | uint256 leftOver = amount - amountToTransfer * _getTotalTransfers(zrc20); 55 | 56 | uint256 lastTransferIndex = destinationTokens[destinationTokens.length - 1] == zrc20 57 | ? destinationTokens.length - 2 58 | : destinationTokens.length - 1; 59 | 60 | for (uint256 i; i < destinationTokens.length; i++) { 61 | address targetZRC20 = destinationTokens[i]; 62 | if (targetZRC20 == zrc20) continue; 63 | 64 | if (lastTransferIndex == i) { 65 | amountToTransfer += leftOver; 66 | } 67 | 68 | uint256 outputAmount = SwapHelperLib._doSwap( 69 | systemContract.wZetaContractAddress(), 70 | systemContract.uniswapv2FactoryAddress(), 71 | systemContract.uniswapv2Router02Address(), 72 | zrc20, 73 | amountToTransfer, 74 | targetZRC20, 75 | 0 76 | ); 77 | SwapHelperLib._doWithdrawal(targetZRC20, outputAmount, BytesHelperLib.addressToBytes(receipient)); 78 | emit Withdrawal(targetZRC20, outputAmount, receipient); 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /packages/zevm-app-contracts/scripts/liquidity-incentives/helpers.ts: -------------------------------------------------------------------------------- 1 | import { BigNumber } from "@ethersproject/bignumber"; 2 | import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; 3 | import { ZetaProtocolNetwork } from "@zetachain/protocol-contracts"; 4 | import { parseEther } from "ethers/lib/utils"; 5 | 6 | import { 7 | ERC20__factory, 8 | RewardDistributor__factory, 9 | RewardDistributorFactory, 10 | SystemContract, 11 | } from "../../typechain-types"; 12 | import { getChainId } from "../address.helpers"; 13 | 14 | export const deployRewardByToken = async ( 15 | deployer: SignerWithAddress, 16 | systemContract: SystemContract, 17 | tokenAddress: string, 18 | rewardDistributorFactory: RewardDistributorFactory 19 | ) => { 20 | const zetaTokenAddress = await systemContract.wZetaContractAddress(); 21 | 22 | const tx = await rewardDistributorFactory.createTokenIncentive( 23 | deployer.address, 24 | deployer.address, 25 | zetaTokenAddress, 26 | // @dev: now we send both tokens so contract calculate internaly LP address 27 | zetaTokenAddress, 28 | tokenAddress 29 | ); 30 | 31 | const receipt = await tx.wait(); 32 | 33 | const event = receipt.events?.find((e) => e.event === "RewardDistributorCreated"); 34 | 35 | const { rewardDistributorContract: rewardDistributorContractAddress } = event?.args as any; 36 | 37 | console.log("RewardDistributor deployed to:", rewardDistributorContractAddress); 38 | 39 | return rewardDistributorContractAddress; 40 | }; 41 | 42 | const getZRC20Address = async (systemContract: SystemContract, network: ZetaProtocolNetwork) => { 43 | const tokenAddress = await systemContract.gasCoinZRC20ByChainId(getChainId(network)); 44 | return tokenAddress; 45 | }; 46 | 47 | export const deployRewardByNetwork = async ( 48 | deployer: SignerWithAddress, 49 | systemContract: SystemContract, 50 | networkName: ZetaProtocolNetwork, 51 | rewardDistributorFactory: RewardDistributorFactory 52 | ) => { 53 | const tokenAddress = await getZRC20Address(systemContract, networkName); 54 | const rewardContractAddress = await deployRewardByToken( 55 | deployer, 56 | systemContract, 57 | tokenAddress, 58 | rewardDistributorFactory 59 | ); 60 | return rewardContractAddress; 61 | }; 62 | 63 | export const addReward = async ( 64 | deployer: SignerWithAddress, 65 | systemContract: SystemContract, 66 | rewardDistributorContractAddress: string, 67 | rewardAmount: BigNumber = parseEther("500"), 68 | rewardDuration: BigNumber = BigNumber.from("604800").mul(8) // 1 week * 8 69 | ) => { 70 | const zetaTokenAddress = await systemContract.wZetaContractAddress(); 71 | 72 | const rewardDistributorContract = await RewardDistributor__factory.connect( 73 | rewardDistributorContractAddress, 74 | deployer 75 | ); 76 | 77 | const ZETA = ERC20__factory.connect(zetaTokenAddress, deployer); 78 | const tx = await ZETA.transfer(rewardDistributorContract.address, rewardAmount.mul(1)); 79 | await tx.wait(); 80 | await rewardDistributorContract.setRewardsDuration(rewardDuration); 81 | await rewardDistributorContract.notifyRewardAmount(rewardAmount); 82 | 83 | console.log("Reward added to:", rewardDistributorContract.address); 84 | }; 85 | -------------------------------------------------------------------------------- /packages/zevm-app-contracts/scripts/uniswap/sell-zeta.ts: -------------------------------------------------------------------------------- 1 | import { parseUnits } from "@ethersproject/units"; 2 | import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; 3 | import { getNonZetaAddress, isProtocolNetworkName, ZetaProtocolNetwork } from "@zetachain/protocol-contracts"; 4 | import { BigNumber } from "ethers"; 5 | import { ethers, network } from "hardhat"; 6 | 7 | import { 8 | ERC20, 9 | ERC20__factory, 10 | IUniswapV2Router02, 11 | SystemContract__factory, 12 | UniswapV2Router02__factory, 13 | } from "../../typechain-types"; 14 | import { getChainId, getSystemContractAddress } from "../address.helpers"; 15 | import { getNow, printReserves } from "./uniswap.helpers"; 16 | 17 | const networkName = network.name; 18 | const SYSTEM_CONTRACT = getSystemContractAddress(); 19 | 20 | const ZETA_TO_SELL = parseUnits("0.001"); 21 | 22 | const swapZeta = async ( 23 | tokenContract: ERC20, 24 | WZETAAddress: string, 25 | ZETAAmountToSell: BigNumber, 26 | uniswapRouter: IUniswapV2Router02, 27 | deployer: SignerWithAddress 28 | ) => { 29 | const tx = await uniswapRouter.swapExactETHForTokens( 30 | 0, 31 | [WZETAAddress, tokenContract.address], 32 | deployer.address, 33 | (await getNow()) + 360, 34 | { gasLimit: 10_000_000, value: ZETAAmountToSell } 35 | ); 36 | await tx.wait(); 37 | 38 | console.log(`Sell tx hash: ${tx.hash}`); 39 | }; 40 | 41 | async function sellZeta( 42 | network: ZetaProtocolNetwork, 43 | WZETAAddress: string, 44 | uniswapFactoryAddress: string, 45 | uniswapRouterAddress: string 46 | ) { 47 | console.log(`Sell ZETA on: ${network}`); 48 | 49 | const [deployer] = await ethers.getSigners(); 50 | 51 | const systemContract = await SystemContract__factory.connect(SYSTEM_CONTRACT, deployer); 52 | const uniswapRouter = await UniswapV2Router02__factory.connect(uniswapRouterAddress, deployer); 53 | 54 | const tokenAddress = await systemContract.gasCoinZRC20ByChainId(getChainId(network)); 55 | const tokenContract = ERC20__factory.connect(tokenAddress, deployer); 56 | 57 | await printReserves(tokenContract, WZETAAddress, uniswapFactoryAddress, deployer); 58 | // await swapZeta(tokenContract, WZETAAddress, ZETA_TO_SELL, uniswapRouter, deployer); 59 | await printReserves(tokenContract, WZETAAddress, uniswapFactoryAddress, deployer); 60 | } 61 | async function main() { 62 | if (!isProtocolNetworkName(networkName)) throw new Error("Invalid network name"); 63 | 64 | const WZETA_ADDRESS = getNonZetaAddress("weth9", networkName); 65 | 66 | const UNISWAP_FACTORY_ADDRESS = getNonZetaAddress("uniswapV2Factory", networkName); 67 | 68 | const UNISWAP_ROUTER_ADDRESS = getNonZetaAddress("uniswapV2Router02", networkName); 69 | 70 | await sellZeta("goerli_testnet", WZETA_ADDRESS, UNISWAP_FACTORY_ADDRESS, UNISWAP_ROUTER_ADDRESS); 71 | await sellZeta("mumbai_testnet", WZETA_ADDRESS, UNISWAP_FACTORY_ADDRESS, UNISWAP_ROUTER_ADDRESS); 72 | await sellZeta("bsc_testnet", WZETA_ADDRESS, UNISWAP_FACTORY_ADDRESS, UNISWAP_ROUTER_ADDRESS); 73 | await sellZeta("btc_testnet", WZETA_ADDRESS, UNISWAP_FACTORY_ADDRESS, UNISWAP_ROUTER_ADDRESS); 74 | } 75 | 76 | main() 77 | .then(() => process.exit(0)) 78 | .catch((error) => { 79 | console.error(error); 80 | process.exit(1); 81 | }); 82 | -------------------------------------------------------------------------------- /packages/zeta-app-contracts/scripts/multi-chain-value/send-value.ts: -------------------------------------------------------------------------------- 1 | import { getAddress, isProtocolNetworkName, ZetaProtocolNetwork } from "@zetachain/protocol-contracts"; 2 | import { BigNumber } from "ethers"; 3 | import { parseEther } from "ethers/lib/utils"; 4 | import { ethers, network } from "hardhat"; 5 | 6 | import { getMultiChainValue } from "../../lib/multi-chain-value/MultiChainValue.helpers"; 7 | import { getErc20 } from "../../lib/shared/deploy.helpers"; 8 | import { MultiChainValue } from "../../typechain-types"; 9 | import { getAppAddress, getChainId } from "../address.helpers"; 10 | 11 | const networkName = network.name; 12 | 13 | const doTranfer = async ( 14 | sourceChain: ZetaProtocolNetwork, 15 | multiChainValueContract: MultiChainValue, 16 | chainId: number, 17 | amount: BigNumber, 18 | destinationAddress: string 19 | ) => { 20 | //@ts-ignore 21 | if (getChainId(sourceChain) == chainId) return; 22 | const ccmGasLimit = 100000; 23 | 24 | if (sourceChain === "zeta_testnet") { 25 | const tx = await multiChainValueContract.sendZeta(chainId, destinationAddress, ccmGasLimit, { value: amount }); 26 | await tx.wait(); 27 | return; 28 | } 29 | 30 | const tx = await multiChainValueContract.send(chainId, destinationAddress, amount, ccmGasLimit); 31 | await tx.wait(); 32 | }; 33 | 34 | const main = async () => { 35 | if (!isProtocolNetworkName(networkName)) throw new Error("Invalid network name"); 36 | //@ts-ignore 37 | const isTestnet = isTestnetNetwork(networkName); 38 | if (!isTestnet) 39 | throw new Error("This script is only for testnet. If you are sure you want to run it, remove this line."); 40 | 41 | const multiChainValueContract = await getMultiChainValue(getAppAddress("multiChainValue", networkName)); 42 | 43 | const [signer] = await ethers.getSigners(); 44 | 45 | const amount = parseEther("1"); 46 | 47 | if (networkName !== "zeta_testnet" && networkName !== "zeta_mainnet") { 48 | const zetaToken = await getErc20(getAddress("zetaToken", networkName)); 49 | const tx = await zetaToken.approve(multiChainValueContract.address, amount.mul(10)); 50 | await tx.wait(); 51 | } 52 | 53 | const destinationAddress = ethers.utils.solidityPack(["address"], [process.env.PUBLIC_KEY_1 ?? signer.address]); 54 | 55 | //@ts-ignore 56 | const isTestnet = isTestnetNetwork(networkName); 57 | 58 | if (isTestnet) { 59 | await doTranfer(networkName, multiChainValueContract, getChainId("goerli_testnet"), amount, destinationAddress); 60 | await doTranfer(networkName, multiChainValueContract, getChainId("mumbai_testnet"), amount, destinationAddress); 61 | await doTranfer(networkName, multiChainValueContract, getChainId("bsc_testnet"), amount, destinationAddress); 62 | await doTranfer(networkName, multiChainValueContract, getChainId("zeta_testnet"), amount, destinationAddress); 63 | } else { 64 | await doTranfer(networkName, multiChainValueContract, getChainId("bsc_mainnet"), amount, destinationAddress); 65 | await doTranfer(networkName, multiChainValueContract, getChainId("zeta_mainnet"), amount, destinationAddress); 66 | await doTranfer(networkName, multiChainValueContract, getChainId("eth_mainnet"), amount, destinationAddress); 67 | } 68 | }; 69 | 70 | main().catch((error) => { 71 | console.error(error); 72 | process.exit(1); 73 | }); 74 | -------------------------------------------------------------------------------- /packages/zevm-app-contracts/test/instant-rewards/instant-rewards-v2.ts: -------------------------------------------------------------------------------- 1 | import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; 2 | import { expect } from "chai"; 3 | import { ethers } from "hardhat"; 4 | 5 | import { InstantRewardsFactory, InstantRewardsV2 } from "../../typechain-types"; 6 | 7 | describe("Instant Rewards Contract test", () => { 8 | let instantRewardsFactory: InstantRewardsFactory, 9 | deployer: SignerWithAddress, 10 | owner: SignerWithAddress, 11 | signer: SignerWithAddress, 12 | user: SignerWithAddress, 13 | addrs: SignerWithAddress[]; 14 | 15 | beforeEach(async () => { 16 | [deployer, owner, signer, user, ...addrs] = await ethers.getSigners(); 17 | const instantRewardsFactoryF = await ethers.getContractFactory("InstantRewardsFactory"); 18 | instantRewardsFactory = (await instantRewardsFactoryF.deploy(owner.address)) as InstantRewardsFactory; 19 | await instantRewardsFactory.deployed(); 20 | }); 21 | 22 | it("Should deploy an IR instance", async () => { 23 | const currentBlock = await ethers.provider.getBlock("latest"); 24 | const start = currentBlock.timestamp + 1000; 25 | const end = start + 1000; 26 | const name = "Instant Rewards"; 27 | const tx = instantRewardsFactory 28 | .connect(owner) 29 | .createInstantRewards(signer.address, start, end, name, "http://img.com", "http://avatar.com", "Description"); 30 | await expect(tx).to.emit(instantRewardsFactory, "InstantRewardsCreated"); 31 | 32 | const events = await instantRewardsFactory.queryFilter("InstantRewardsCreated"); 33 | const address = events[0].args?.instantRewards; 34 | expect(address).to.be.not.undefined; 35 | 36 | const instantRewards = (await ethers.getContractAt("InstantRewardsV2", address)) as InstantRewardsV2; 37 | expect(await instantRewards.signerAddress()).to.be.eq(signer.address); 38 | expect(await instantRewards.start()).to.be.eq(start); 39 | expect(await instantRewards.end()).to.be.eq(end); 40 | expect(await instantRewards.name()).to.be.eq(name); 41 | 42 | await instantRewards.connect(owner).acceptOwnership(); 43 | expect(await instantRewards.owner()).to.be.eq(owner.address); 44 | }); 45 | 46 | it("Should revert if not owner try to deploy", async () => { 47 | const currentBlock = await ethers.provider.getBlock("latest"); 48 | const start = currentBlock.timestamp + 1000; 49 | const end = start + 1000; 50 | const name = "Instant Rewards"; 51 | const tx = instantRewardsFactory.createInstantRewards( 52 | signer.address, 53 | start, 54 | end, 55 | name, 56 | "http://img.com", 57 | "http://avatar.com", 58 | "Description" 59 | ); 60 | await expect(tx).to.revertedWith("AccessDenied"); 61 | }); 62 | 63 | it("Should deploy an IR instance with any wallet if it's open", async () => { 64 | await instantRewardsFactory.connect(owner).setAllowPublicCreation(true); 65 | 66 | const currentBlock = await ethers.provider.getBlock("latest"); 67 | const start = currentBlock.timestamp + 1000; 68 | const end = start + 1000; 69 | const name = "Instant Rewards"; 70 | const tx = instantRewardsFactory.createInstantRewards( 71 | signer.address, 72 | start, 73 | end, 74 | name, 75 | "http://img.com", 76 | "http://avatar.com", 77 | "Description" 78 | ); 79 | await expect(tx).to.emit(instantRewardsFactory, "InstantRewardsCreated"); 80 | }); 81 | }); 82 | -------------------------------------------------------------------------------- /packages/zevm-app-contracts/test/Swap.spec.ts: -------------------------------------------------------------------------------- 1 | import { BigNumber } from "@ethersproject/bignumber"; 2 | import { parseUnits } from "@ethersproject/units"; 3 | import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; 4 | import { getNonZetaAddress } from "@zetachain/protocol-contracts"; 5 | import { expect } from "chai"; 6 | import { ethers, network } from "hardhat"; 7 | 8 | import { getBitcoinTxMemoForTest, getSwapParams } from "../scripts/zeta-swap/helpers"; 9 | import { 10 | MockSystemContract, 11 | MockZRC20, 12 | ZetaSwap, 13 | ZetaSwap__factory, 14 | ZetaSwapBtcInbound, 15 | ZetaSwapBtcInbound__factory, 16 | } from "../typechain-types"; 17 | import { evmSetup } from "./test.helpers"; 18 | 19 | describe("ZetaSwap tests", () => { 20 | let zetaSwapContract: ZetaSwap; 21 | let zetaSwapBTCContract: ZetaSwapBtcInbound; 22 | let ZRC20Contracts: MockZRC20[]; 23 | let systemContract: MockSystemContract; 24 | 25 | let accounts: SignerWithAddress[]; 26 | let deployer: SignerWithAddress; 27 | 28 | beforeEach(async () => { 29 | accounts = await ethers.getSigners(); 30 | [deployer] = accounts; 31 | 32 | await network.provider.send("hardhat_setBalance", [deployer.address, parseUnits("1000000").toHexString()]); 33 | 34 | const uniswapRouterAddr = getNonZetaAddress("uniswapV2Router02", "eth_mainnet"); 35 | 36 | const uniswapFactoryAddr = getNonZetaAddress("uniswapV2Factory", "eth_mainnet"); 37 | 38 | const wGasToken = getNonZetaAddress("weth9", "eth_mainnet"); 39 | 40 | const evmSetupResult = await evmSetup(wGasToken, uniswapFactoryAddr, uniswapRouterAddr); 41 | ZRC20Contracts = evmSetupResult.ZRC20Contracts; 42 | systemContract = evmSetupResult.systemContract; 43 | 44 | const FactorySwap = (await ethers.getContractFactory("ZetaSwap")) as ZetaSwap__factory; 45 | zetaSwapContract = (await FactorySwap.deploy(systemContract.address)) as ZetaSwap; 46 | await zetaSwapContract.deployed(); 47 | 48 | const FactoryBTC = (await ethers.getContractFactory("ZetaSwapBtcInbound")) as ZetaSwapBtcInbound__factory; 49 | zetaSwapBTCContract = (await FactoryBTC.deploy(systemContract.address)) as ZetaSwapBtcInbound; 50 | await zetaSwapBTCContract.deployed(); 51 | }); 52 | 53 | describe("zetaSwap", () => { 54 | it("Should do swap", async () => { 55 | const amount = parseUnits("10"); 56 | await ZRC20Contracts[0].transfer(systemContract.address, amount); 57 | 58 | const initBalance = await ZRC20Contracts[1].balanceOf(deployer.address); 59 | 60 | const params = getSwapParams(deployer.address, ZRC20Contracts[1].address, BigNumber.from(0)); 61 | await systemContract.onCrossChainCall(zetaSwapContract.address, ZRC20Contracts[0].address, amount, params); 62 | 63 | const endBalance = await ZRC20Contracts[1].balanceOf(deployer.address); 64 | expect(endBalance).to.be.gt(initBalance); 65 | }); 66 | }); 67 | 68 | describe("zetaSwapBTC", () => { 69 | it("Should do swap", async () => { 70 | const amount = parseUnits("1"); 71 | await ZRC20Contracts[0].transfer(systemContract.address, amount); 72 | 73 | const initBalance = await ZRC20Contracts[1].balanceOf(deployer.address); 74 | 75 | const params = getBitcoinTxMemoForTest(deployer.address, "5"); 76 | await systemContract.onCrossChainCall(zetaSwapBTCContract.address, ZRC20Contracts[0].address, amount, params); 77 | 78 | const endBalance = await ZRC20Contracts[1].balanceOf(deployer.address); 79 | expect(endBalance).to.be.gt(initBalance); 80 | }); 81 | }); 82 | }); 83 | -------------------------------------------------------------------------------- /packages/zevm-app-contracts/scripts/instant-rewards/deploy-v2.ts: -------------------------------------------------------------------------------- 1 | import { isProtocolNetworkName } from "@zetachain/protocol-contracts"; 2 | import { ethers, network } from "hardhat"; 3 | 4 | import { InstantRewardsFactory, InstantRewardsFactory__factory } from "../../typechain-types"; 5 | import { instantRewards } from "../../typechain-types/contracts"; 6 | import { saveAddress } from "../address.helpers"; 7 | import { verifyContract } from "../explorer.helpers"; 8 | 9 | const networkName = network.name; 10 | 11 | const OWNERS = { 12 | zeta_mainnet: "0xD7E8bD37db625a4856E056D2617C9d140dB99182", 13 | zeta_testnet: "0x1d24d94520B94B26351f6573de5ef9731c48531A", 14 | }; 15 | 16 | //@ts-ignore 17 | const owner = OWNERS[networkName]; 18 | console.log("Owner:", owner); 19 | 20 | const deployInstantRewardsSample = async (instantRewardsFactoryAddress: string) => { 21 | const [deployer] = await ethers.getSigners(); 22 | 23 | const InstantRewardsFactory = new InstantRewardsFactory__factory(deployer); 24 | const instantRewards = InstantRewardsFactory.attach(instantRewardsFactoryAddress); 25 | 26 | // get current timestamp from ethers 27 | const block = await ethers.provider.getBlock("latest"); 28 | const timestamp = block.timestamp; 29 | const start = timestamp + 60 * 60 * 24 * 7; // 1 week from now 30 | const end = start + 60 * 60 * 24 * 7 * 4; // 4 weeks from start 31 | 32 | const params = [ 33 | owner, 34 | start, 35 | end, 36 | "ZetaChain", 37 | "https://zetachain.io", 38 | "https://zetachain.io/logo.png", 39 | "ZetaChain description", 40 | ]; 41 | 42 | const tx = await instantRewards.createInstantRewards(...params, { 43 | gasLimit: 25000000, 44 | }); 45 | 46 | const rec = await tx.wait(); 47 | 48 | // query event InstantRewardsCreated to get the address 49 | const event = rec.events?.find((event) => event.event === "InstantRewardsCreated"); 50 | 51 | if (!event) throw new Error("InstantRewardsCreated event not found"); 52 | //@ts-ignore 53 | const instantRewardsAddress = event.args[0]; 54 | if (!instantRewardsAddress) throw new Error("InstantRewards address not found"); 55 | console.log("InstantRewards deployed to:", instantRewardsAddress); 56 | 57 | await verifyContract( 58 | instantRewardsAddress, 59 | [owner, ...params], 60 | "contracts/instant-rewards/InstantRewardsV2.sol:InstantRewardsV2" 61 | ); 62 | }; 63 | 64 | const deployInstantRewards = async () => { 65 | if (!isProtocolNetworkName(networkName)) throw new Error("Invalid network name"); 66 | 67 | const InstantRewardsFactory = (await ethers.getContractFactory( 68 | "InstantRewardsFactory" 69 | )) as InstantRewardsFactory__factory; 70 | const instantRewards = await InstantRewardsFactory.deploy(owner); 71 | 72 | await instantRewards.deployed(); 73 | 74 | console.log("InstantRewards deployed to:", instantRewards.address); 75 | 76 | saveAddress("InstantRewardsFactory", instantRewards.address, networkName); 77 | 78 | await verifyContract(instantRewards.address, [owner]); 79 | // await verifyContract("0xAf5693bBC958e442462F411F46421e389c7A8602", [owner]); 80 | 81 | return instantRewards; 82 | }; 83 | 84 | const main = async () => { 85 | if (!isProtocolNetworkName(networkName)) throw new Error("Invalid network name"); 86 | const instantRewards = await deployInstantRewards(); 87 | await deployInstantRewardsSample(instantRewards.address); 88 | // await deployInstantRewardsSample("0x3A557fe83FD734f21DD35E98f546B9706d486F55"); 89 | }; 90 | 91 | main().catch((error) => { 92 | console.error(error); 93 | process.exit(1); 94 | }); 95 | -------------------------------------------------------------------------------- /scripts/setup-tutorial.ts: -------------------------------------------------------------------------------- 1 | import { Wallet } from "ethers"; 2 | import * as fs from "fs"; 3 | import * as https from "https"; 4 | 5 | const jsonPath = "scripts/.wallet.json"; 6 | 7 | interface WalletConfig { 8 | address: string; 9 | privateKey: string; 10 | } 11 | 12 | function saveEnvFiles(address: string, privateKey: string): void { 13 | const value: string = ` 14 | PRIVATE_KEY=${privateKey} 15 | ZETA_NETWORK=athens 16 | EXECUTE_PROGRAMMATICALLY=true`; 17 | const filePaths: string[] = ["packages/example-contracts/.env", "packages/zevm-example-contracts/.env"]; 18 | 19 | filePaths.forEach((filePath: string) => { 20 | fs.writeFile(filePath, value, (err: NodeJS.ErrnoException | null) => { 21 | if (err) { 22 | console.error(`Failed to write to file ${filePath}: ${err}`); 23 | } else { 24 | console.log(`Updated .env file "${filePath}".`); 25 | } 26 | }); 27 | }); 28 | } 29 | function saveWalletFile(address: string, privateKey: string, jsonPath: string): void { 30 | const data = `{"address": "${address}", "privateKey": "${privateKey}"}`; 31 | 32 | fs.access(jsonPath, (err) => { 33 | if (err) { 34 | // The file does not exist, so we can write to it 35 | fs.writeFile(jsonPath, data, (err) => { 36 | if (err) throw err; 37 | console.log("The new wallet file has been saved!"); 38 | }); 39 | } else { 40 | // The file already exists, so we do not write to it 41 | // console.log("The file already exists!"); 42 | } 43 | }); 44 | } 45 | function callFaucet(address: string): void { 46 | // Hit Faucet to get some testnet Zeta 47 | console.log("Requesting testnet assets from the faucet..."); 48 | const options: https.RequestOptions = { 49 | hostname: "faucet.zetachain.link", 50 | method: "GET", 51 | path: `/eth/${address}`, 52 | port: 443, 53 | }; 54 | 55 | const req = https.request(options, (res) => { 56 | console.log(`statusCode: ${res.statusCode}`); 57 | 58 | res.on("data", (d) => { 59 | process.stdout.write(d); 60 | }); 61 | }); 62 | 63 | req.on("error", (error) => { 64 | console.error(error); 65 | }); 66 | 67 | req.end(); 68 | } 69 | 70 | function createWallet(filePath: string, callback: () => void): void { 71 | fs.access(filePath, (err) => { 72 | if (!err) { 73 | callback(); 74 | } 75 | }); 76 | } 77 | 78 | async function getOrCreateWallet(filePath: string): Promise { 79 | let wallet: Wallet; 80 | 81 | try { 82 | const data = await fs.promises.readFile(filePath, "utf8"); 83 | const walletConfig: WalletConfig = JSON.parse(data); 84 | wallet = new Wallet(walletConfig.privateKey); 85 | } catch (error) { 86 | console.log("No Wallet Exists, Creating One..."); 87 | const newWallet = Wallet.createRandom(); 88 | const walletConfig: WalletConfig = { 89 | address: newWallet.address, 90 | privateKey: newWallet.privateKey, 91 | }; 92 | await fs.promises.writeFile(filePath, JSON.stringify(walletConfig)); 93 | wallet = newWallet; 94 | } 95 | 96 | return wallet; 97 | } 98 | 99 | const wallet = getOrCreateWallet(jsonPath).then(async (wallet) => { 100 | console.log(`Your Wallet Address: ${wallet.address}`); 101 | console.log(`Your Private Key: ${wallet.privateKey.substring(2)}`); 102 | saveEnvFiles(wallet.address, wallet.privateKey.substring(2)); 103 | saveWalletFile(wallet.address, wallet.privateKey.substring(2), jsonPath); 104 | await callFaucet(wallet.address); 105 | }); 106 | -------------------------------------------------------------------------------- /packages/zevm-app-contracts/contracts/instant-rewards/InstantRewards.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.20; 3 | 4 | import "@openzeppelin/contracts/security/Pausable.sol"; 5 | import "@openzeppelin/contracts/access/Ownable.sol"; 6 | import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; 7 | import "@openzeppelin/contracts/utils/cryptography/EIP712.sol"; 8 | import {SignatureChecker} from "@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol"; 9 | 10 | contract InstantRewards is Ownable, Pausable, ReentrancyGuard, EIP712 { 11 | bytes32 private constant CLAIM_TYPEHASH = 12 | keccak256("Claim(address to,uint256 sigExpiration,bytes32 taskId,uint256 amount)"); 13 | 14 | struct ClaimData { 15 | address to; 16 | bytes signature; 17 | uint256 sigExpiration; 18 | bytes32 taskId; 19 | uint256 amount; 20 | } 21 | 22 | mapping(address => mapping(bytes32 => bool)) public taskCompletedByUser; 23 | 24 | address public signerAddress; 25 | 26 | event SignerUpdated(address indexed signerAddress); 27 | event Claimed(address indexed to, bytes32 indexed taskId, uint256 amount); 28 | event Withdrawn(address indexed wallet, uint256 amount); 29 | 30 | error InvalidSigner(); 31 | error SignatureExpired(); 32 | error InvalidAddress(); 33 | error TaskAlreadyClaimed(); 34 | error TransferFailed(); 35 | 36 | constructor(address signerAddress_, address owner) Ownable() EIP712("InstantRewards", "1") { 37 | if (signerAddress_ == address(0)) revert InvalidAddress(); 38 | transferOwnership(owner); 39 | signerAddress = signerAddress_; 40 | } 41 | 42 | function _verify(ClaimData memory claimData) private view { 43 | bytes32 structHash = keccak256( 44 | abi.encode(CLAIM_TYPEHASH, claimData.to, claimData.sigExpiration, claimData.taskId, claimData.amount) 45 | ); 46 | bytes32 constructedHash = _hashTypedDataV4(structHash); 47 | 48 | if (!SignatureChecker.isValidSignatureNow(signerAddress, constructedHash, claimData.signature)) { 49 | revert InvalidSigner(); 50 | } 51 | 52 | if (block.timestamp > claimData.sigExpiration) revert SignatureExpired(); 53 | } 54 | 55 | function claim(ClaimData memory claimData) public virtual nonReentrant whenNotPaused { 56 | claimData.to = msg.sender; 57 | _verify(claimData); 58 | 59 | if (taskCompletedByUser[claimData.to][claimData.taskId]) revert TaskAlreadyClaimed(); 60 | 61 | taskCompletedByUser[claimData.to][claimData.taskId] = true; 62 | 63 | (bool success, ) = claimData.to.call{value: claimData.amount}(""); 64 | if (!success) revert TransferFailed(); 65 | 66 | emit Claimed(claimData.to, claimData.taskId, claimData.amount); 67 | } 68 | 69 | function setSignerAddress(address signerAddress_) external onlyOwner { 70 | if (signerAddress_ == address(0)) revert InvalidAddress(); 71 | signerAddress = signerAddress_; 72 | emit SignerUpdated(signerAddress_); 73 | } 74 | 75 | function withdraw(address wallet, uint256 amount) public virtual onlyOwner { 76 | if (wallet == address(0)) revert InvalidAddress(); 77 | if (amount > address(this).balance) revert TransferFailed(); 78 | (bool success, ) = wallet.call{value: amount}(""); 79 | if (!success) revert TransferFailed(); 80 | 81 | emit Withdrawn(wallet, amount); 82 | } 83 | 84 | function pause() external onlyOwner { 85 | _pause(); 86 | } 87 | 88 | function unpause() external onlyOwner { 89 | _unpause(); 90 | } 91 | 92 | receive() external payable {} 93 | } 94 | -------------------------------------------------------------------------------- /packages/zevm-app-contracts/contracts/shared/MockSystemContract.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.7; 3 | 4 | import "@zetachain/protocol-contracts/contracts/zevm/interfaces/zContract.sol"; 5 | import "@zetachain/protocol-contracts/contracts/zevm/interfaces/IZRC20.sol"; 6 | 7 | interface SystemContractErrors { 8 | error CallerIsNotFungibleModule(); 9 | 10 | error InvalidTarget(); 11 | 12 | error CantBeIdenticalAddresses(); 13 | 14 | error CantBeZeroAddress(); 15 | } 16 | 17 | contract MockSystemContract is SystemContractErrors { 18 | error TransferFailed(); 19 | 20 | mapping(uint256 => uint256) public gasPriceByChainId; 21 | mapping(uint256 => address) public gasCoinZRC20ByChainId; 22 | mapping(uint256 => address) public gasZetaPoolByChainId; 23 | 24 | address public wZetaContractAddress; 25 | address public immutable uniswapv2FactoryAddress; 26 | address public immutable uniswapv2Router02Address; 27 | 28 | event SystemContractDeployed(); 29 | event SetGasPrice(uint256, uint256); 30 | event SetGasCoin(uint256, address); 31 | event SetGasZetaPool(uint256, address); 32 | event SetWZeta(address); 33 | 34 | constructor(address wzeta_, address uniswapv2Factory_, address uniswapv2Router02_) { 35 | wZetaContractAddress = wzeta_; 36 | uniswapv2FactoryAddress = uniswapv2Factory_; 37 | uniswapv2Router02Address = uniswapv2Router02_; 38 | emit SystemContractDeployed(); 39 | } 40 | 41 | // fungible module updates the gas price oracle periodically 42 | function setGasPrice(uint256 chainID, uint256 price) external { 43 | gasPriceByChainId[chainID] = price; 44 | emit SetGasPrice(chainID, price); 45 | } 46 | 47 | function setGasCoinZRC20(uint256 chainID, address zrc20) external { 48 | gasCoinZRC20ByChainId[chainID] = zrc20; 49 | emit SetGasCoin(chainID, zrc20); 50 | } 51 | 52 | function setWZETAContractAddress(address addr) external { 53 | wZetaContractAddress = addr; 54 | emit SetWZeta(wZetaContractAddress); 55 | } 56 | 57 | // returns sorted token addresses, used to handle return values from pairs sorted in this order 58 | function sortTokens(address tokenA, address tokenB) internal pure returns (address token0, address token1) { 59 | if (tokenA == tokenB) revert CantBeIdenticalAddresses(); 60 | (token0, token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA); 61 | if (token0 == address(0)) revert CantBeZeroAddress(); 62 | } 63 | 64 | function uniswapv2PairFor(address factory, address tokenA, address tokenB) public pure returns (address pair) { 65 | (address token0, address token1) = sortTokens(tokenA, tokenB); 66 | pair = address( 67 | uint160( 68 | uint256( 69 | keccak256( 70 | abi.encodePacked( 71 | hex"ff", 72 | factory, 73 | keccak256(abi.encodePacked(token0, token1)), 74 | hex"96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f" // init code hash 75 | ) 76 | ) 77 | ) 78 | ) 79 | ); 80 | } 81 | 82 | function onCrossChainCall(address target, address zrc20, uint256 amount, bytes calldata message) external { 83 | zContext memory context = zContext({sender: msg.sender, origin: "", chainID: block.chainid}); 84 | bool transfer = IZRC20(zrc20).transfer(target, amount); 85 | if (!transfer) revert TransferFailed(); 86 | zContract(target).onCrossChainCall(context, zrc20, amount, message); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /packages/zevm-app-contracts/contracts/proof-of-liveness/ProofOfLiveness.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.20; 3 | 4 | import "hardhat/console.sol"; 5 | 6 | contract ProofOfLiveness { 7 | uint256 constant PROOF_PERIOD = 24 hours; 8 | uint256 constant LAST_PERIODS_LENGTH = 5; 9 | 10 | // Mapping to track the proof history for each user (last 5 proof timestamps) 11 | mapping(address => uint256[LAST_PERIODS_LENGTH]) public proofHistory; 12 | 13 | // Custom error for when a user has already proved liveness within the last PROOF_PERIOD 14 | error ProofWithinLast24Hours(uint256 lastProofTime); 15 | 16 | // Event to log when liveness is proved 17 | event LivenessProved(address indexed user, uint256 proofTimestamp); 18 | 19 | // The function to prove liveness, can only be called once every PROOF_PERIOD 20 | function proveLiveness() external { 21 | uint256 currentTime = block.timestamp; 22 | uint256 lastProofTime = proofHistory[msg.sender][0]; // The most recent proof timestamp is always stored in the first position 23 | 24 | // Check if the user has proved liveness within the last PROOF_PERIOD 25 | if (currentTime < lastProofTime + PROOF_PERIOD) { 26 | revert ProofWithinLast24Hours(lastProofTime); 27 | } 28 | 29 | // Shift the proof history and add the new timestamp 30 | _updateProofHistory(msg.sender, currentTime); 31 | 32 | // Emit an event to track the liveness proof 33 | emit LivenessProved(msg.sender, currentTime); 34 | } 35 | 36 | // Helper function to check if a user can prove liveness (returns true if PROOF_PERIOD has passed) 37 | function canProveLiveness(address user) external view returns (bool) { 38 | uint256 currentTime = block.timestamp; 39 | return currentTime >= proofHistory[user][0] + PROOF_PERIOD; 40 | } 41 | 42 | // View function to return the liveness proof status for the last LAST_PERIODS_LENGTH periods (each PROOF_PERIOD long) 43 | function getLastPeriodsStatus(address user) external view returns (bool[LAST_PERIODS_LENGTH] memory) { 44 | uint256 currentTime = block.timestamp; 45 | bool[LAST_PERIODS_LENGTH] memory proofStatus; 46 | 47 | for (uint256 i = 0; i < LAST_PERIODS_LENGTH; i++) { 48 | // Calculate the end of the period (going back i * PROOF_PERIOD) 49 | uint256 periodEnd = currentTime - (i * PROOF_PERIOD); 50 | uint256 periodStart = periodEnd - PROOF_PERIOD - 1; 51 | // If the proof timestamp falls within this period, mark it as true 52 | proofStatus[i] = hasProofedAt(user, periodStart, periodEnd); 53 | } 54 | 55 | return proofStatus; 56 | } 57 | 58 | function hasProofedAt(address user, uint256 periodStart, uint256 periodEnd) public view returns (bool) { 59 | for (uint256 i = 0; i < LAST_PERIODS_LENGTH; i++) { 60 | if (proofHistory[user][i] >= periodStart && proofHistory[user][i] < periodEnd) { 61 | return true; 62 | } 63 | } 64 | return false; 65 | } 66 | 67 | function getProofHistory(address user) external view returns (uint256[LAST_PERIODS_LENGTH] memory) { 68 | return proofHistory[user]; 69 | } 70 | 71 | // Internal function to update the user's proof history by shifting timestamps and adding the new proof 72 | function _updateProofHistory(address user, uint256 newProofTimestamp) internal { 73 | // Shift the history to the right 74 | for (uint256 i = LAST_PERIODS_LENGTH - 1; i > 0; i--) { 75 | proofHistory[user][i] = proofHistory[user][i - 1]; 76 | } 77 | 78 | // Add the new timestamp in the first position 79 | proofHistory[user][0] = newProofTimestamp; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /packages/zeta-app-contracts/scripts/multi-chain-value/search-zbtc-tx-cache.ts: -------------------------------------------------------------------------------- 1 | export const precalculatedResults = [ 2 | "0xb59145790f705b023fedee9d803642b24221bacc8b285464b58d90d637872b30", 3 | "0xb78d08dc5e8051cec1427fe9343d65e3996fb21ba8f3483c4348dbe72f0e04ce", 4 | "0xc1dfa2f66e9c729d1e85fd2eaf8c5fbd2aee71f7b21d62a94e6d2b216b256fe2", 5 | "0xe07f7758634148491a340128ade1fd9e27c4a0ca02beb7a52c2cedb36dff5976", 6 | "0xdcd9ca7a9be91d10cb22df5e7b850844f26d144eb1884fd0ce1a3a3820358f17", 7 | "0xf5797d3188a54d586d11f4729a87eddfa46b9c8a2e17db79894a4ba428aa954e", 8 | "0xc7aa5681e715c403614635335a04026ad025080a828f60a031437cb22f42df11", 9 | "0xc47f55d77b27f3880bac796a09b53d0aeaae38cea8acd99d3d890d3518cfe580", 10 | "0x78f7b45ccdb7c3266a8a5d7575470001daea7396e58fe232e9c3a749446727f7", 11 | "0xb59145790f705b023fedee9d803642b24221bacc8b285464b58d90d637872b30", 12 | "0xb78d08dc5e8051cec1427fe9343d65e3996fb21ba8f3483c4348dbe72f0e04ce", 13 | "0xc1dfa2f66e9c729d1e85fd2eaf8c5fbd2aee71f7b21d62a94e6d2b216b256fe2", 14 | "0xe07f7758634148491a340128ade1fd9e27c4a0ca02beb7a52c2cedb36dff5976", 15 | "0xdcd9ca7a9be91d10cb22df5e7b850844f26d144eb1884fd0ce1a3a3820358f17", 16 | "0xf5797d3188a54d586d11f4729a87eddfa46b9c8a2e17db79894a4ba428aa954e", 17 | "0xc7aa5681e715c403614635335a04026ad025080a828f60a031437cb22f42df11", 18 | "0xc47f55d77b27f3880bac796a09b53d0aeaae38cea8acd99d3d890d3518cfe580", 19 | "0x78f7b45ccdb7c3266a8a5d7575470001daea7396e58fe232e9c3a749446727f7", 20 | "0xddb732c20c3ac963a1a16eb78d7af2b2c7a73468afd7baf92ef38097c0fc12de", 21 | "0x50e69755f7a815e1ddfc9ef091ea19a7b95099f3ad8befeddf8c2fcd01553c91", 22 | "0xfc57adb390a22a8e54c4ff6cf7280270002d6553185bd90d800b45cf72d7e203", 23 | "0x05a21a369f3045e53d31341e9bd8880254611a4eaa88b00090ec8c542b3fe882", 24 | "0x70c718c714d957ed34930420da379125eb6eaff70cf0103e14e7e3f2afb22542", 25 | "0x5e18c3f68f3b964af8b31d9276329e7e61ffb5d36881a9fc95cee56dea391984", 26 | "0x9bffb2df710d2036ad2dbcfdf5ac3f3783c92e10b55e74fc84bb1fcdc61e31b4", 27 | "0x52e44224ded45bc18ad03cfa6042f244a74b4d3e4778d0cf40a1201e03666c19", 28 | "0x83ae01f16be764e94360ae2f3b8ae219a4c388b0dc2d010993c9b57e53dc1674", 29 | "0xbec21c8e1a411c8397111d7ea666b58a5fcee9eab15e8cf26484c6b46754dad7", 30 | "0x9ac1ec1b723afa4f7a41c5a92199a21575be47c55f815fddf3b6ae8d3404c4bf", 31 | "0x86e41fcd6928e7435bcc925d16f523c7b5e2d611e29e563747eeeb4f19254bef", 32 | "0xa62c69ed5c4632160cfe3ab21fe4e6b1ce24e46642722ea9624c1c6a2123d479", 33 | "0x2e75fc8b803f406f75e01dd61272a1bd291e4943fe50bf65925053f95df7c5ba", 34 | "0x9aaefece38fd2bd87077038a63fffb7c84cc8dd1ed01de134a8504a1f9a410c3", 35 | "0xa1e0d5b75eb08b4f60ffaea7fbcf5090fb75e2e018887799989eea3b12c3e10a", 36 | "0x81126c18c7ca7d1fb7ded6644a87802e91bf52154ee4af7a5b379354e24fb6e0", 37 | "0x6c60cc1218dfa0af9c12bb529f8f21747df53faa1bb995cd60e86e76212a5600", 38 | "0x41ae1ecbf323fc39f149e7f9638800c9540b943d26f6d39002edd64452d85fa4", 39 | "0xcc03addcc86b53cad457b2cc459cde4c8cd68565e1f3abebfbf838bee7dc96ae", 40 | "0x36107b56c9dcf776712abbb0eef95d12e3099f821e163574440046e9b3baee0b", 41 | "0x2f705185db776988b6817fc6ac04d320e81fedb85a522941428321851bec6d9f", 42 | "0x386684d9a6156cc8f54cd34003e147a84bfeaa7c4558f1f10642e913378ce2c5", 43 | "0x4ccb24a326418411562ae0969100ff65a18e60a24558fe503f9defc3a250d411", 44 | "0xd27a67caefe97d4abfdf1c7d1a53cb411a58604c5eabda2e48a26d96cab099e8", 45 | "0xbf9a1b8bc7a1e918a2fc1795e0bb3df0ed4b7146161325580c47fa69c8e8369b", 46 | "0x1e155d7c9e30ab5c65a096cc92f4a40e9cb8d8c0b0416182a5e4784455b4e6cc", 47 | "0x0010d928235a05092dea55eeb4ed45944ff49cb7ec749b01793aadc2c8c74f54", 48 | "0xaf5a6622984421de67f18ab65c565e74d9047d8f38a7266dd8cd14370823f4df", 49 | "0xa059d7149e57c331d3fb13e36c1657160085dcbd5a8c6b01d0f48d271ab60614", 50 | "0x47c482df41abffb5b6827e13afc379b2f843bac2759c6d0eb1d00459f9cb31c5", 51 | "0x77b46aada73de00fa67aa4f72ac47257ddb96b4352851ca3664368bbf0d0ea23", 52 | ]; 53 | -------------------------------------------------------------------------------- /packages/zevm-app-contracts/test/proof-of-liveness/proof-of-liveness.ts: -------------------------------------------------------------------------------- 1 | import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; 2 | import { expect } from "chai"; 3 | import { ethers } from "hardhat"; 4 | 5 | import { ProofOfLiveness } from "../../typechain-types"; 6 | 7 | const PROOF_PERIOD = 24 * 60 * 60; // 24 hours in seconds 8 | 9 | describe("Proof Of Liveness Contract test", () => { 10 | let proofOfLiveness: ProofOfLiveness, 11 | owner: SignerWithAddress, 12 | signer: SignerWithAddress, 13 | user: SignerWithAddress, 14 | addrs: SignerWithAddress[]; 15 | 16 | beforeEach(async () => { 17 | [owner, signer, user, ...addrs] = await ethers.getSigners(); 18 | const ProofOfLivenessFactory = await ethers.getContractFactory("ProofOfLiveness"); 19 | 20 | proofOfLiveness = await ProofOfLivenessFactory.deploy(); 21 | 22 | await proofOfLiveness.deployed(); 23 | }); 24 | 25 | it("Should proof", async () => { 26 | const tx = await proofOfLiveness.proveLiveness(); 27 | 28 | const receipt = await tx.wait(); 29 | const blockTimestamp = (await ethers.provider.getBlock(receipt.blockNumber)).timestamp; 30 | 31 | await expect(tx).to.emit(proofOfLiveness, "LivenessProved").withArgs(owner.address, blockTimestamp); 32 | }); 33 | 34 | it("Should proof 5 times every 24 hours and return correct view values", async () => { 35 | // Prove liveness 5 times 36 | for (let i = 0; i < 5; i++) { 37 | // Call the proveLiveness function 38 | const tx = await proofOfLiveness.proveLiveness(); 39 | await tx.wait(); 40 | 41 | // Increase the time by 24 hours in the EVM 42 | await ethers.provider.send("evm_increaseTime", [PROOF_PERIOD]); 43 | await ethers.provider.send("evm_mine", []); // Mine a new block to apply the time change 44 | } 45 | 46 | // Now check the getLastPeriodsStatus for the owner 47 | const periodsStatus = await proofOfLiveness.getLastPeriodsStatus(owner.address); 48 | 49 | // We expect that all 5 periods should return true 50 | expect(periodsStatus).to.deep.equal([true, true, true, true, true]); 51 | }); 52 | 53 | it("Should proof 5 times every 24 hours and return correct view values if one day is missing", async () => { 54 | // Prove liveness 5 times 55 | for (let i = 0; i < 5; i++) { 56 | // Call the proveLiveness function if day is not 3 57 | if (i !== 3) { 58 | const tx = await proofOfLiveness.proveLiveness(); 59 | await tx.wait(); 60 | } 61 | 62 | // Increase the time by 24 hours in the EVM 63 | await ethers.provider.send("evm_increaseTime", [PROOF_PERIOD]); 64 | await ethers.provider.send("evm_mine", []); // Mine a new block to apply the time change 65 | } 66 | 67 | // Now check the getLastPeriodsStatus for the owner 68 | const periodsStatus = await proofOfLiveness.getLastPeriodsStatus(owner.address); 69 | 70 | // We expect that all 5 periods should return true but 3 71 | expect(periodsStatus).to.deep.equal([true, false, true, true, true]); 72 | }); 73 | 74 | it("Should proof view return if only one day was proof", async () => { 75 | const tx = await proofOfLiveness.proveLiveness(); 76 | await tx.wait(); 77 | await ethers.provider.send("evm_mine", []); // Mine a new block to apply the time change 78 | 79 | // Now check the getLastPeriodsStatus for the owner 80 | const periodsStatus = await proofOfLiveness.getLastPeriodsStatus(owner.address); 81 | 82 | expect(periodsStatus).to.deep.equal([true, false, false, false, false]); 83 | }); 84 | 85 | it("Should revert if trying to prove twice in less than 24 hours", async () => { 86 | // Prove liveness for the first time 87 | await proofOfLiveness.proveLiveness(); 88 | 89 | const tx = proofOfLiveness.proveLiveness(); 90 | 91 | await expect(tx).to.be.revertedWith("ProofWithinLast24Hours"); 92 | }); 93 | }); 94 | -------------------------------------------------------------------------------- /packages/zevm-app-contracts/contracts/shared/SwapHelperLib.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity =0.8.7; 3 | 4 | import "@uniswap/v2-periphery/contracts/interfaces/IUniswapV2Router02.sol"; 5 | import "@uniswap/v2-periphery/contracts/interfaces/IUniswapV2Router01.sol"; 6 | import "@zetachain/protocol-contracts/contracts/zevm/interfaces/IZRC20.sol"; 7 | 8 | library SwapHelperLib { 9 | uint16 internal constant MAX_DEADLINE = 200; 10 | 11 | error WrongGasContract(); 12 | 13 | error NotEnoughToPayGasFee(); 14 | 15 | error CantBeIdenticalAddresses(); 16 | 17 | error CantBeZeroAddress(); 18 | 19 | // returns sorted token addresses, used to handle return values from pairs sorted in this order 20 | function sortTokens(address tokenA, address tokenB) internal pure returns (address token0, address token1) { 21 | if (tokenA == tokenB) revert CantBeIdenticalAddresses(); 22 | (token0, token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA); 23 | if (token0 == address(0)) revert CantBeZeroAddress(); 24 | } 25 | 26 | // calculates the CREATE2 address for a pair without making any external calls 27 | function uniswapv2PairFor(address factory, address tokenA, address tokenB) public pure returns (address pair) { 28 | (address token0, address token1) = sortTokens(tokenA, tokenB); 29 | pair = address( 30 | uint160( 31 | uint256( 32 | keccak256( 33 | abi.encodePacked( 34 | hex"ff", 35 | factory, 36 | keccak256(abi.encodePacked(token0, token1)), 37 | hex"96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f" // init code hash 38 | ) 39 | ) 40 | ) 41 | ) 42 | ); 43 | } 44 | 45 | function _doWithdrawal(address targetZRC20, uint256 amount, bytes32 receipient) internal { 46 | (address gasZRC20, uint256 gasFee) = IZRC20(targetZRC20).withdrawGasFee(); 47 | 48 | if (gasZRC20 != targetZRC20) revert WrongGasContract(); 49 | if (gasFee >= amount) revert NotEnoughToPayGasFee(); 50 | 51 | IZRC20(targetZRC20).approve(targetZRC20, gasFee); 52 | IZRC20(targetZRC20).withdraw(abi.encodePacked(receipient), amount - gasFee); 53 | } 54 | 55 | function _existsPairPool(address uniswapV2Factory, address zrc20A, address zrc20B) internal view returns (bool) { 56 | address uniswapPool = uniswapv2PairFor(uniswapV2Factory, zrc20A, zrc20B); 57 | return IZRC20(zrc20A).balanceOf(uniswapPool) > 0 && IZRC20(zrc20B).balanceOf(uniswapPool) > 0; 58 | } 59 | 60 | function _doSwap( 61 | address zetaToken, 62 | address uniswapV2Factory, 63 | address uniswapV2Router, 64 | address zrc20, 65 | uint256 amount, 66 | address targetZRC20, 67 | uint256 minAmountOut 68 | ) internal returns (uint256) { 69 | bool existsPairPool = _existsPairPool(uniswapV2Factory, zrc20, targetZRC20); 70 | 71 | address[] memory path; 72 | if (existsPairPool) { 73 | path = new address[](2); 74 | path[0] = zrc20; 75 | path[1] = targetZRC20; 76 | } else { 77 | path = new address[](3); 78 | path[0] = zrc20; 79 | path[1] = zetaToken; 80 | path[2] = targetZRC20; 81 | } 82 | 83 | IZRC20(zrc20).approve(address(uniswapV2Router), amount); 84 | uint256[] memory amounts = IUniswapV2Router01(uniswapV2Router).swapExactTokensForTokens( 85 | amount, 86 | minAmountOut, 87 | path, 88 | address(this), 89 | block.timestamp + MAX_DEADLINE 90 | ); 91 | return amounts[path.length - 1]; 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /packages/zevm-app-contracts/scripts/zeta-swap/stress-swap.ts: -------------------------------------------------------------------------------- 1 | /* ********************************* */ 2 | /* How to run this script 3 | /* To run this script you have to setup the enviroment (yarn and setup .env) and run this command 4 | /* ZETA_NETWORK=athens npx hardhat run scripts/zeta-swap/stress-swap.ts --network "polygon-mumbai" 5 | /* In the example we use "polygon-mumbai", that's the source network. Can be any valid source network 6 | /* Be sure to have enough gas tokens to pay all the tx 7 | /* This script is to stress the node but it's not optimize for real use. May fail to enquee some tx sometimes but will enquee enough to test 8 | /* ********************************* */ 9 | 10 | import { BigNumber } from "@ethersproject/bignumber"; 11 | import { formatEther, parseEther } from "@ethersproject/units"; 12 | import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; 13 | import { 14 | getAddress, 15 | getZRC20Address, 16 | isProtocolNetworkName, 17 | ZetaProtocolNetwork, 18 | zetaProtocolNetworks, 19 | } from "@zetachain/protocol-contracts"; 20 | import { ethers, network } from "hardhat"; 21 | 22 | import { getZEVMAppAddress } from "../address.helpers"; 23 | import { getSwapData } from "./helpers"; 24 | 25 | const networkName = network.name; 26 | 27 | interface SwapToChainParams { 28 | destinationNetwork: ZetaProtocolNetwork; 29 | nonce: number; 30 | signer: SignerWithAddress; 31 | tssAddress: string; 32 | value: BigNumber; 33 | zetaSwapAddress: string; 34 | } 35 | 36 | const swapToChain = async ({ 37 | zetaSwapAddress, 38 | tssAddress, 39 | signer, 40 | destinationNetwork, 41 | value, 42 | nonce, 43 | }: SwapToChainParams) => { 44 | const data = getSwapData(zetaSwapAddress, signer.address, getZRC20Address(destinationNetwork), BigNumber.from("0")); 45 | const tx = await signer.sendTransaction({ 46 | data, 47 | nonce, 48 | to: tssAddress, 49 | value, 50 | }); 51 | console.log(`tx: ${tx.hash}, nonce: ${nonce}, destinationToken: ${destinationNetwork}, value: ${formatEther(value)}`); 52 | }; 53 | 54 | const main = async () => { 55 | if (!isProtocolNetworkName(networkName)) throw new Error("Invalid network name"); 56 | const swappableNetwork: ZetaProtocolNetwork = networkName; 57 | 58 | // @dev: bitcoin is invalid as destination 59 | const invalidDestinations: ZetaProtocolNetwork[] = [swappableNetwork, "btc_testnet"]; 60 | const networks = zetaProtocolNetworks.map((c) => c as ZetaProtocolNetwork); 61 | 62 | const destinationNetworks = networks.filter((e) => !invalidDestinations.includes(e)); 63 | 64 | console.log(`Swapping native token...`); 65 | 66 | const [signer] = await ethers.getSigners(); 67 | 68 | const zetaSwapAddress = getZEVMAppAddress("zetaSwap"); 69 | 70 | const tssAddress = getAddress("tss", swappableNetwork); 71 | 72 | const nonce = await signer.getTransactionCount(); 73 | 74 | const swapsPerNetwork = destinationNetworks.map((destinationNetwork, index) => { 75 | const baseNonce = nonce + index * 5; 76 | const param: SwapToChainParams = { 77 | destinationNetwork, 78 | nonce: baseNonce, 79 | signer, 80 | tssAddress, 81 | value: parseEther("0.002"), 82 | zetaSwapAddress, 83 | }; 84 | return [ 85 | swapToChain(param), 86 | swapToChain({ ...param, nonce: baseNonce + 1, value: parseEther("0.002") }), 87 | swapToChain({ ...param, nonce: baseNonce + 2, value: parseEther("0.003") }), 88 | swapToChain({ ...param, nonce: baseNonce + 3, value: parseEther("0.004") }), 89 | swapToChain({ ...param, nonce: baseNonce + 4, value: parseEther("0.005") }), 90 | ]; 91 | }); 92 | 93 | const swaps = swapsPerNetwork.reduce((p, c) => [...p, ...c], []); 94 | 95 | await Promise.all(swaps); 96 | }; 97 | 98 | main().catch((error) => { 99 | console.error(error); 100 | process.exit(1); 101 | }); 102 | -------------------------------------------------------------------------------- /packages/zevm-app-contracts/scripts/uniswap/sell-token.ts: -------------------------------------------------------------------------------- 1 | import { MaxUint256 } from "@ethersproject/constants"; 2 | import { parseUnits } from "@ethersproject/units"; 3 | import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; 4 | import { getNonZetaAddress, isProtocolNetworkName, ZetaProtocolNetwork } from "@zetachain/protocol-contracts"; 5 | import { BigNumber } from "ethers"; 6 | import { ethers, network } from "hardhat"; 7 | 8 | import { 9 | ERC20, 10 | ERC20__factory, 11 | IUniswapV2Router02, 12 | SystemContract__factory, 13 | UniswapV2Router02__factory, 14 | } from "../../typechain-types"; 15 | import { getChainId, getSystemContractAddress } from "../address.helpers"; 16 | import { getNow, printReserves } from "./uniswap.helpers"; 17 | 18 | const networkName = network.name; 19 | const SYSTEM_CONTRACT = getSystemContractAddress(); 20 | 21 | const BTC_TO_SELL = parseUnits("0", 8); 22 | const ETH_TO_SELL = parseUnits("0"); 23 | const MATIC_TO_SELL = parseUnits("0"); 24 | const BNB_TO_SELL = parseUnits("0"); 25 | 26 | const swapZeta = async ( 27 | tokenContract: ERC20, 28 | WZETAAddress: string, 29 | amountIn: BigNumber, 30 | uniswapRouter: IUniswapV2Router02, 31 | deployer: SignerWithAddress 32 | ) => { 33 | const tx1 = await tokenContract.approve(uniswapRouter.address, MaxUint256); 34 | await tx1.wait(); 35 | 36 | console.log("Uniswap approved to consume token..."); 37 | 38 | const tx2 = await uniswapRouter.swapExactTokensForETH( 39 | amountIn, 40 | 0, 41 | [tokenContract.address, WZETAAddress], 42 | deployer.address, 43 | (await getNow()) + 360, 44 | { gasLimit: 10_000_000 } 45 | ); 46 | await tx2.wait(); 47 | 48 | console.log(`Tx hash: ${tx2.hash}`); 49 | }; 50 | 51 | async function sellToken( 52 | network: ZetaProtocolNetwork, 53 | tokenAmountToSell: BigNumber, 54 | WZETAAddress: string, 55 | uniswapFactoryAddress: string, 56 | uniswapRouterAddress: string 57 | ) { 58 | console.log(`Selling token on: ${network}`); 59 | 60 | const [deployer] = await ethers.getSigners(); 61 | 62 | const systemContract = await SystemContract__factory.connect(SYSTEM_CONTRACT, deployer); 63 | const uniswapRouter = await UniswapV2Router02__factory.connect(uniswapRouterAddress, deployer); 64 | 65 | const tokenAddress = await systemContract.gasCoinZRC20ByChainId(getChainId(network)); 66 | const tokenContract = ERC20__factory.connect(tokenAddress, deployer); 67 | 68 | await printReserves(tokenContract, WZETAAddress, uniswapFactoryAddress, deployer); 69 | // await swapZeta(tokenContract, WZETAAddress, tokenAmountToSell, uniswapRouter, deployer); 70 | await printReserves(tokenContract, WZETAAddress, uniswapFactoryAddress, deployer); 71 | } 72 | async function main() { 73 | if (!isProtocolNetworkName(networkName)) throw new Error("Invalid network name"); 74 | 75 | const WZETA_ADDRESS = getNonZetaAddress("weth9", networkName); 76 | 77 | const UNISWAP_FACTORY_ADDRESS = getNonZetaAddress("uniswapV2Factory", networkName); 78 | 79 | const UNISWAP_ROUTER_ADDRESS = getNonZetaAddress("uniswapV2Router02", networkName); 80 | 81 | if (!ETH_TO_SELL.isZero()) { 82 | await sellToken("goerli_testnet", ETH_TO_SELL, WZETA_ADDRESS, UNISWAP_FACTORY_ADDRESS, UNISWAP_ROUTER_ADDRESS); 83 | } 84 | if (!MATIC_TO_SELL.isZero()) { 85 | await sellToken("mumbai_testnet", MATIC_TO_SELL, WZETA_ADDRESS, UNISWAP_FACTORY_ADDRESS, UNISWAP_ROUTER_ADDRESS); 86 | } 87 | if (!BNB_TO_SELL.isZero()) { 88 | await sellToken("bsc_testnet", BNB_TO_SELL, WZETA_ADDRESS, UNISWAP_FACTORY_ADDRESS, UNISWAP_ROUTER_ADDRESS); 89 | } 90 | if (!BTC_TO_SELL.isZero()) { 91 | await sellToken("btc_testnet", BTC_TO_SELL, WZETA_ADDRESS, UNISWAP_FACTORY_ADDRESS, UNISWAP_ROUTER_ADDRESS); 92 | } 93 | } 94 | 95 | main() 96 | .then(() => process.exit(0)) 97 | .catch((error) => { 98 | console.error(error); 99 | process.exit(1); 100 | }); 101 | -------------------------------------------------------------------------------- /packages/zevm-app-contracts/test/xp-nft/xp-nft-v2.ts: -------------------------------------------------------------------------------- 1 | import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; 2 | import { expect } from "chai"; 3 | import { ethers, upgrades } from "hardhat"; 4 | 5 | import { ZetaXP_V2 } from "../../typechain-types"; 6 | import { getSelLevelSignature, getSignature, NFT, NFTSigned } from "./test.helpers"; 7 | 8 | const ZETA_BASE_URL = "https://api.zetachain.io/nft/"; 9 | const HARDHAT_CHAIN_ID = 1337; 10 | 11 | const encodeTag = (tag: string) => ethers.utils.keccak256(ethers.utils.defaultAbiCoder.encode(["string"], [tag])); 12 | 13 | const getTokenIdFromRecipient = (receipt: any): number => { 14 | //@ts-ignore 15 | return receipt.events[0].args?.tokenId; 16 | }; 17 | 18 | describe("XP NFT V2 Contract test", () => { 19 | let zetaXP: ZetaXP_V2, signer: SignerWithAddress, user: SignerWithAddress, addrs: SignerWithAddress[]; 20 | let sampleNFT: NFT; 21 | 22 | beforeEach(async () => { 23 | [signer, user, ...addrs] = await ethers.getSigners(); 24 | const zetaXPFactory = await ethers.getContractFactory("ZetaXP"); 25 | 26 | zetaXP = await upgrades.deployProxy(zetaXPFactory, [ 27 | "ZETA NFT", 28 | "ZNFT", 29 | ZETA_BASE_URL, 30 | signer.address, 31 | signer.address, 32 | ]); 33 | 34 | await zetaXP.deployed(); 35 | 36 | const ZetaXPFactory = await ethers.getContractFactory("ZetaXP_V2"); 37 | zetaXP = await upgrades.upgradeProxy(zetaXP.address, ZetaXPFactory); 38 | 39 | const tag = encodeTag("XP_NFT"); 40 | 41 | sampleNFT = { 42 | tag, 43 | to: user.address, 44 | tokenId: undefined, 45 | }; 46 | }); 47 | 48 | const mintNFT = async (nft: NFT) => { 49 | const currentBlock = await ethers.provider.getBlock("latest"); 50 | const sigTimestamp = currentBlock.timestamp; 51 | const signatureExpiration = sigTimestamp + 1000; 52 | 53 | const signature = await getSignature( 54 | HARDHAT_CHAIN_ID, 55 | zetaXP.address, 56 | signer, 57 | signatureExpiration, 58 | sigTimestamp, 59 | nft.to, 60 | nft 61 | ); 62 | 63 | const nftParams: NFTSigned = { 64 | ...sampleNFT, 65 | sigTimestamp, 66 | signature, 67 | signatureExpiration, 68 | } as NFTSigned; 69 | 70 | const tx = await zetaXP.mintNFT(nftParams); 71 | const receipt = await tx.wait(); 72 | return getTokenIdFromRecipient(receipt); 73 | }; 74 | 75 | it("Should update NFT level", async () => { 76 | const tokenId = await mintNFT(sampleNFT); 77 | 78 | const currentBlock = await ethers.provider.getBlock("latest"); 79 | const sigTimestamp = currentBlock.timestamp; 80 | const signatureExpiration = sigTimestamp + 1000; 81 | const level = 3; 82 | 83 | const signature = await getSelLevelSignature( 84 | HARDHAT_CHAIN_ID, 85 | zetaXP.address, 86 | signer, 87 | signatureExpiration, 88 | sigTimestamp, 89 | tokenId, 90 | level 91 | ); 92 | 93 | await zetaXP.setLevel({ level, sigTimestamp, signature, signatureExpiration, tokenId }); 94 | const onchainLevel = await zetaXP.getLevel(tokenId); 95 | expect(onchainLevel).to.be.eq(level); 96 | }); 97 | 98 | it("Should emit event on set level", async () => { 99 | const tokenId = await mintNFT(sampleNFT); 100 | 101 | const currentBlock = await ethers.provider.getBlock("latest"); 102 | const sigTimestamp = currentBlock.timestamp; 103 | const signatureExpiration = sigTimestamp + 1000; 104 | const level = 3; 105 | 106 | const signature = await getSelLevelSignature( 107 | HARDHAT_CHAIN_ID, 108 | zetaXP.address, 109 | signer, 110 | signatureExpiration, 111 | sigTimestamp, 112 | tokenId, 113 | level 114 | ); 115 | 116 | const tx = zetaXP.setLevel({ level, sigTimestamp, signature, signatureExpiration, tokenId }); 117 | await expect(tx).to.emit(zetaXP, "LevelSet").withArgs(signer.address, tokenId, level); 118 | }); 119 | }); 120 | -------------------------------------------------------------------------------- /packages/zevm-app-contracts/scripts/uniswap/remove-liquidity-zeta-uniswap.ts: -------------------------------------------------------------------------------- 1 | import { MaxUint256 } from "@ethersproject/constants"; 2 | import { formatUnits } from "@ethersproject/units"; 3 | import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; 4 | import { getNonZetaAddress, isProtocolNetworkName, ZetaProtocolNetwork } from "@zetachain/protocol-contracts"; 5 | import { BigNumber } from "ethers"; 6 | import { ethers, network } from "hardhat"; 7 | 8 | import { 9 | ERC20, 10 | ERC20__factory, 11 | IUniswapV2Factory__factory, 12 | IUniswapV2Pair, 13 | IUniswapV2Pair__factory, 14 | IUniswapV2Router02, 15 | SystemContract__factory, 16 | UniswapV2Router02__factory, 17 | } from "../../typechain-types"; 18 | import { getChainId, getSystemContractAddress } from "../address.helpers"; 19 | import { getNow, printReserves, sortPair } from "./uniswap.helpers"; 20 | 21 | const networkName = network.name; 22 | const SYSTEM_CONTRACT = getSystemContractAddress(); 23 | 24 | const removeTokenEthLiquidity = async ( 25 | tokenContract: ERC20, 26 | LPContract: IUniswapV2Pair, 27 | LPAmountToRemove: BigNumber, 28 | uniswapRouter: IUniswapV2Router02, 29 | deployer: SignerWithAddress 30 | ) => { 31 | const tx1 = await LPContract.approve(uniswapRouter.address, MaxUint256); 32 | await tx1.wait(); 33 | 34 | console.log("Uniswap approved to consume LP..."); 35 | 36 | const tx2 = await uniswapRouter.removeLiquidityETH( 37 | tokenContract.address, 38 | LPAmountToRemove, 39 | 0, 40 | 0, 41 | deployer.address, 42 | (await getNow()) + 360, 43 | { gasLimit: 10_000_000 } 44 | ); 45 | await tx2.wait(); 46 | }; 47 | 48 | async function removeLiquidity( 49 | network: ZetaProtocolNetwork, 50 | WZETAAddress: string, 51 | uniswapFactoryAddress: string, 52 | uniswapRouterAddress: string 53 | ) { 54 | console.log(`Removing liquidity for: ${network}`); 55 | 56 | const [deployer] = await ethers.getSigners(); 57 | 58 | const systemContract = await SystemContract__factory.connect(SYSTEM_CONTRACT, deployer); 59 | const uniswapV2Factory = IUniswapV2Factory__factory.connect(uniswapFactoryAddress, deployer); 60 | const uniswapRouter = await UniswapV2Router02__factory.connect(uniswapRouterAddress, deployer); 61 | 62 | const tokenAddress = await systemContract.gasCoinZRC20ByChainId(getChainId(network)); 63 | const tokenContract = ERC20__factory.connect(tokenAddress, deployer); 64 | 65 | const pair = sortPair(tokenAddress, WZETAAddress); 66 | 67 | const poolAddress = await uniswapV2Factory.getPair(pair.TokenA, pair.TokenB); 68 | 69 | const pool = IUniswapV2Pair__factory.connect(poolAddress, deployer); 70 | 71 | const LPBalance = await pool.balanceOf(deployer.address); 72 | 73 | console.log(`LP Balance: ${formatUnits(LPBalance, 18)} for ${poolAddress}`); 74 | 75 | await printReserves(tokenContract, WZETAAddress, uniswapFactoryAddress, deployer); 76 | // await removeTokenEthLiquidity(tokenContract, pool, LPBalance, uniswapRouter, deployer); 77 | await printReserves(tokenContract, WZETAAddress, uniswapFactoryAddress, deployer); 78 | } 79 | async function main() { 80 | if (!isProtocolNetworkName(networkName)) throw new Error("Invalid network name"); 81 | 82 | const WZETA_ADDRESS = getNonZetaAddress("weth9", networkName); 83 | 84 | const UNISWAP_FACTORY_ADDRESS = getNonZetaAddress("uniswapV2Factory", networkName); 85 | 86 | const UNISWAP_ROUTER_ADDRESS = getNonZetaAddress("uniswapV2Router02", networkName); 87 | 88 | await removeLiquidity("goerli_testnet", WZETA_ADDRESS, UNISWAP_FACTORY_ADDRESS, UNISWAP_ROUTER_ADDRESS); 89 | await removeLiquidity("mumbai_testnet", WZETA_ADDRESS, UNISWAP_FACTORY_ADDRESS, UNISWAP_ROUTER_ADDRESS); 90 | await removeLiquidity("bsc_testnet", WZETA_ADDRESS, UNISWAP_FACTORY_ADDRESS, UNISWAP_ROUTER_ADDRESS); 91 | await removeLiquidity("btc_testnet", WZETA_ADDRESS, UNISWAP_FACTORY_ADDRESS, UNISWAP_ROUTER_ADDRESS); 92 | } 93 | 94 | main() 95 | .then(() => process.exit(0)) 96 | .catch((error) => { 97 | console.error(error); 98 | process.exit(1); 99 | }); 100 | --------------------------------------------------------------------------------