├── .solhintignore ├── .prettierrc ├── .solcover.js ├── audits ├── Certik-2020-nov-09.pdf ├── Knownsec-2020-oct-22.pdf ├── PeckShield-2022-Sep.pdf ├── 2020-12-27-PeckShield.pdf └── PeckShield-2020-sep-10.pdf ├── contracts ├── enclaves │ ├── interfaces │ │ ├── ICredit.sol │ │ └── IAttestationVerifier.sol │ ├── AttestationAutherSample.sol │ └── AttestationAutherUpgradeable.sol ├── mocks │ ├── Invoke.sol │ ├── IUSDC.sol │ └── ArbGasInfo.sol ├── staking │ ├── tree │ │ └── Errors.sol │ └── interfaces │ │ ├── IClusterRewards.sol │ │ ├── IStakeManager.sol │ │ ├── IClusterRegistry.sol │ │ ├── IRewardDelegators.sol │ │ └── IClusterSelector.sol ├── token │ ├── IArbToken.sol │ ├── Pond.sol │ └── Credit.sol └── lock │ └── LockUpgradeable.sol ├── .env.example ├── tsconfig.json ├── .solhint.json ├── test ├── config │ └── staking.json ├── helpers │ ├── common.ts │ ├── erc165.ts │ └── rbac.ts └── governance │ ├── Timelock.ts │ └── GovernorAlpha.ts ├── pyproject.toml ├── addresses └── 421614.json ├── deployments ├── enclaves │ ├── deployMarketV1.ts │ ├── deployAttestationVerifier.ts │ ├── config.json │ ├── MarketV1.ts │ └── AttestationVerifier.ts ├── utils │ ├── networks.js │ ├── helpers.js │ └── Upgrade.ts ├── staking │ ├── verifyContracts.ts │ ├── deployStaking.ts │ ├── RewardDelegatorsInit.ts │ ├── RewardDelegators.ts │ ├── ClusterRegistry.ts │ ├── StakeManager.ts │ ├── ClusterRewards.ts │ ├── ReceiverStaking.ts │ ├── ClusterSelector.ts │ └── config.json ├── arbBridge │ ├── L2GatewayUpgrade.ts │ ├── L1GatewayUpgrade.ts │ ├── L1GatewayDeploy.ts │ ├── L2GatewayDeploy.ts │ ├── L2GatewayInit.ts │ └── L1GatewayInit.ts ├── tokens │ ├── MPondNew.ts │ ├── PondNew.ts │ ├── pond.js │ └── mpond.js └── bridge │ └── Bridge.ts ├── .gitignore ├── utils ├── fuzzer.ts ├── testSuite.ts └── typechainConvertor.ts ├── scripts ├── interact.ts ├── rbac │ ├── MPondWhitelist.ts │ ├── GrantRole.ts │ └── RevokeRole.ts ├── deploy │ ├── Upgrade.ts │ ├── token │ │ ├── MPond.ts │ │ ├── Pond.ts │ │ └── Bridge.ts │ ├── enclaves │ │ ├── call.ts │ │ ├── Credit.ts │ │ ├── MarketV1.ts │ │ └── UpgradeMarketV1.ts │ └── staking │ │ ├── ClusterRegistry.ts │ │ ├── RewardDelegators.ts │ │ ├── RewardDelegatorsInit.ts │ │ ├── ClusterRewards.ts │ │ └── StakeManager.ts ├── erc20 │ └── Transfer.ts ├── arbStake.ts ├── helper │ └── helper.ts └── populateTree.ts ├── benchmarks ├── helpers │ ├── util.ts │ └── deployment.ts ├── fixtures │ ├── ClusterSelector.ts │ └── ClusterRewards.ts ├── Pond.ts └── ClusterSelector.ts ├── package.json ├── address.json ├── hardhat.config.ts ├── poetry.lock └── .openzeppelin └── unknown-250.json /.solhintignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 140 3 | } 4 | -------------------------------------------------------------------------------- /.solcover.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | skipFiles: ["staking/interfaces", "staking/lib"], 3 | }; 4 | -------------------------------------------------------------------------------- /audits/Certik-2020-nov-09.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marlinprotocol/contracts/HEAD/audits/Certik-2020-nov-09.pdf -------------------------------------------------------------------------------- /audits/Knownsec-2020-oct-22.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marlinprotocol/contracts/HEAD/audits/Knownsec-2020-oct-22.pdf -------------------------------------------------------------------------------- /audits/PeckShield-2022-Sep.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marlinprotocol/contracts/HEAD/audits/PeckShield-2022-Sep.pdf -------------------------------------------------------------------------------- /audits/2020-12-27-PeckShield.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marlinprotocol/contracts/HEAD/audits/2020-12-27-PeckShield.pdf -------------------------------------------------------------------------------- /audits/PeckShield-2020-sep-10.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marlinprotocol/contracts/HEAD/audits/PeckShield-2020-sep-10.pdf -------------------------------------------------------------------------------- /contracts/enclaves/interfaces/ICredit.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.26; 3 | 4 | interface ICredit { 5 | function redeemAndBurn(address _to, uint256 _amount) external; 6 | } 7 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | ETHERSCAN_API_KEY= 2 | INFURA_API_KEY= 3 | ALCHEMY_KEY= 4 | 5 | GOERLI_DEPLOYER_KEY= 6 | ARBITRUM_SEPOLIA_DEPLOYER_KEY= 7 | LINEA_GOERLI_DEPLOYER_KEY= 8 | 9 | ARBISCAN_API_KEY= 10 | LINEASCAN_API_KEY= 11 | INFURA_API_KEY= -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2018", 4 | "module": "commonjs", 5 | "strict": true, 6 | "esModuleInterop": true, 7 | "outDir": "dist" 8 | }, 9 | "include": ["./scripts", "./test", "./deployments", "./utils", "./benchmarks"], 10 | "files": ["./hardhat.config.ts"] 11 | } -------------------------------------------------------------------------------- /.solhint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "solhint:default", 3 | "plugins": [], 4 | "rules": { 5 | "const-name-snakecase": "off", 6 | "avoid-suicide": "error", 7 | "avoid-sha3": "warn", 8 | "not-rely-on-block-hash": "warn", 9 | "max-line-length": ["warn", 140], 10 | "no-unused-imports": "warn", 11 | "imports-order": "warn" 12 | } 13 | } -------------------------------------------------------------------------------- /test/config/staking.json: -------------------------------------------------------------------------------- 1 | { 2 | "undelegationWaitTime": 22, 3 | "rewardPerEpoch": 10000, 4 | "minMPONDStake": 2, 5 | "payoutDenomination": 100000, 6 | "PondRewardFactor": 100, 7 | "MPondRewardFactor": 100, 8 | "PondWeightForThreshold": 0, 9 | "MPondWeightForThreshold": 1000000, 10 | "PondWeightForDelegation": 1, 11 | "MPondWeightForDelegation": 1000000 12 | } -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "contracts" 3 | version = "0.1.0" 4 | description = "" 5 | authors = ["Roshan Raghupathy "] 6 | license = "MIT" 7 | 8 | [tool.poetry.dependencies] 9 | python = "^3.9" 10 | 11 | [tool.poetry.dev-dependencies] 12 | slither-analyzer = "^0.8.1" 13 | 14 | [build-system] 15 | requires = ["poetry-core>=1.0.0"] 16 | build-backend = "poetry.core.masonry.api" 17 | -------------------------------------------------------------------------------- /addresses/421614.json: -------------------------------------------------------------------------------- 1 | { 2 | "proxy": { 3 | "usdc": "0x2e6566bBcE6F3400427849A26e273847f1D7Ed2C", 4 | "marketV1": "0xDcD2846DCA523Db1C8F3c842a41A58099dE26A0A", 5 | "credit": "0x1343d88885eE888CEe79FEb3DfD0C5fC8fd65Af1" 6 | }, 7 | "implementation": { 8 | "marketV1": "0x573B6a07d1Eb414B9CED0707DEca19b07798D105", 9 | "credit": "0x587A50Fd13161503384A2a431b838868Db0b3b39" 10 | } 11 | } -------------------------------------------------------------------------------- /contracts/mocks/Invoke.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | interface IClusterSelector { 5 | function selectClusters() external returns (address[] memory _selectedClusters); 6 | } 7 | 8 | contract Invoke { 9 | constructor() {} 10 | 11 | function selectClusters(IClusterSelector clusterSelector) public returns (address[] memory) { 12 | return clusterSelector.selectClusters(); 13 | } 14 | } -------------------------------------------------------------------------------- /contracts/staking/tree/Errors.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | library Errors { 6 | string constant ZERO_INDEX_INVALID = "1"; 7 | string constant CANT_INSERT_ZERO_ADDRESS = "2"; 8 | string constant ADDRESS_ALREADY_EXISTS = "3"; 9 | string constant ADDRESS_DOESNT_EXIST = "4"; 10 | string constant INVALID_INIT_STATE = "5"; 11 | string constant CLUSTER_SELECTION_NOT_COMPLETE = "6"; 12 | } 13 | -------------------------------------------------------------------------------- /deployments/enclaves/deployMarketV1.ts: -------------------------------------------------------------------------------- 1 | import { ethers } from 'hardhat'; 2 | import { deploy } from './MarketV1'; 3 | 4 | async function deployMarketV1() { 5 | let chainId = (await ethers.provider.getNetwork()).chainId; 6 | console.log("Chain Id:", chainId); 7 | 8 | await deploy(); 9 | } 10 | 11 | deployMarketV1() 12 | .then(() => process.exit(0)) 13 | .catch((error) => { 14 | console.error(error); 15 | process.exit(1); 16 | }); 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | vendor 3 | build 4 | slither.log 5 | .env 6 | *merged.sol 7 | temp 8 | .DS_Store 9 | accounts.json 10 | cache 11 | artifacts 12 | coverage 13 | coverage.json 14 | .openzeppelin/unknown-31337.json 15 | .openzeppelin/unknown-421611.json 16 | .openzeppelin/unknown-421613.json 17 | .openzeppelin/*-9876543210.json 18 | .gitsigners 19 | typechain-types/* 20 | *.log 21 | .vscode 22 | 23 | # Local Network 24 | addresses/31337.json 25 | 26 | .openzeppelin -------------------------------------------------------------------------------- /deployments/enclaves/deployAttestationVerifier.ts: -------------------------------------------------------------------------------- 1 | import { ethers } from 'hardhat'; 2 | import { deploy } from './AttestationVerifier'; 3 | 4 | async function deployAttestationVerifier() { 5 | let chainId = (await ethers.provider.getNetwork()).chainId; 6 | console.log("Chain Id:", chainId); 7 | 8 | await deploy(); 9 | } 10 | 11 | deployAttestationVerifier() 12 | .then(() => process.exit(0)) 13 | .catch((error) => { 14 | console.error(error); 15 | process.exit(1); 16 | }); 17 | -------------------------------------------------------------------------------- /deployments/utils/networks.js: -------------------------------------------------------------------------------- 1 | const network = { 2 | MAINNET: "https://mainnet.infura.io/v3/9dc997986f8840daa0e6ccb1d8d0d757", 3 | ROPSTEN: "https://ropsten.infura.io/v3/9dc997986f8840daa0e6ccb1d8d0d757", 4 | KOVAN: "https://kovan.infura.io/v3/9dc997986f8840daa0e6ccb1d8d0d757", 5 | GOERLI: "https://goerli.infura.io/v3/9dc997986f8840daa0e6ccb1d8d0d757", 6 | RINKEBY: "https://rinkeby.infura.io/v3/9dc997986f8840daa0e6ccb1d8d0d757", 7 | MATIC: "https://rpc-mainnet.matic.network", 8 | }; 9 | 10 | module.exports = network; 11 | -------------------------------------------------------------------------------- /contracts/enclaves/interfaces/IAttestationVerifier.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | interface IAttestationVerifier { 6 | function verify( 7 | bytes memory attestation, 8 | bytes memory enclaveKey, 9 | bytes memory PCR0, 10 | bytes memory PCR1, 11 | bytes memory PCR2, 12 | uint256 enclaveCPUs, 13 | uint256 enclaveMemory, 14 | uint256 timestamp 15 | ) external view; 16 | function verify(bytes memory data) external view; 17 | } 18 | -------------------------------------------------------------------------------- /contracts/mocks/IUSDC.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | import "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol"; 5 | import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; 6 | import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; 7 | 8 | 9 | interface IUSDCMock { 10 | function mint(address to, uint256 amount) external; 11 | function decimals() external pure returns (uint8); 12 | function grantRole(bytes32 role, address account) external; 13 | } -------------------------------------------------------------------------------- /deployments/staking/verifyContracts.ts: -------------------------------------------------------------------------------- 1 | import { verify as verifyClusterRegistry } from "./ClusterRegistry"; 2 | import { verify as verifyClusterRewards } from "./ClusterRewards"; 3 | import { verify as verifyClusterSelector } from "./ClusterSelector"; 4 | import { verify as verifyReceiverStaking } from "./ReceiverStaking"; 5 | import { verify as verifyRewardDelegators } from "./RewardDelegators"; 6 | import { verify as verifyStakeManager } from "./StakeManager"; 7 | 8 | async function verifyAll() { 9 | await verifyClusterRegistry(); 10 | await verifyClusterRewards(); 11 | await verifyClusterSelector("ETH"); 12 | await verifyReceiverStaking(); 13 | await verifyRewardDelegators(); 14 | await verifyStakeManager(); 15 | } 16 | 17 | verifyAll(); -------------------------------------------------------------------------------- /deployments/utils/helpers.js: -------------------------------------------------------------------------------- 1 | async function deployContract(web3, abi, bytecode, arguments, config) { 2 | const contract = new web3.eth.Contract(abi); 3 | const receiptPromise = new Promise((resolve, reject) => { 4 | contract 5 | .deploy({ 6 | data: bytecode, 7 | arguments, 8 | }) 9 | .send({ 10 | from: config.from, 11 | gas: config.gas, 12 | gasPrice: config.gasPrice, 13 | }) 14 | .on("transactionHash", console.log) 15 | .on("receipt", (receipt) => { 16 | resolve(receipt.contractAddress); 17 | }) 18 | .on("error", (error) => { 19 | reject(error); 20 | }); 21 | }); 22 | 23 | return receiptPromise; 24 | } 25 | 26 | module.exports = deployContract; 27 | -------------------------------------------------------------------------------- /contracts/mocks/ArbGasInfo.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | interface IArbGasInfo { 5 | function getPricesInArbGas() external view returns (uint, uint, uint); 6 | } 7 | 8 | contract ArbGasInfo is IArbGasInfo { 9 | uint256 public perL2Tx; 10 | uint256 public gasForL1Calldata; 11 | uint256 public storageArbGas; 12 | 13 | constructor() {} 14 | 15 | function setPrices(uint256 _perL2Tx, uint256 _gasForL1Calldata, uint256 _storageArbGas) public { 16 | perL2Tx = _perL2Tx; 17 | gasForL1Calldata = _gasForL1Calldata; 18 | storageArbGas = _storageArbGas; 19 | } 20 | 21 | function getPricesInArbGas() external view override returns (uint, uint, uint) { 22 | return (perL2Tx, gasForL1Calldata, storageArbGas); 23 | } 24 | } -------------------------------------------------------------------------------- /utils/fuzzer.ts: -------------------------------------------------------------------------------- 1 | import { BigNumber, BigNumberish, utils } from "ethers"; 2 | import { keccak256, randomBytes } from "ethers/lib/utils"; 3 | 4 | export class FuzzedNumber { 5 | static random(): BigNumber { 6 | const bytes = randomBytes(32); 7 | return BigNumber.from(bytes); 8 | } 9 | 10 | static randomInRange(min: BigNumberish, max: BigNumberish): BigNumber { 11 | if (BigNumber.from(max).lte(min)) { 12 | throw new Error("max should be more than min"); 13 | } 14 | const bytes = randomBytes(32); 15 | const diff = BigNumber.from(max).sub(min); 16 | return BigNumber.from(bytes).mod(diff).add(min); 17 | } 18 | } 19 | 20 | export class FuzzedAddress { 21 | static random(rand = "123"): string { 22 | let address = keccak256(Buffer.from(rand + new Date().valueOf().toString())) 23 | .toString() 24 | .slice(0, 42); 25 | return utils.getAddress(address); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /utils/testSuite.ts: -------------------------------------------------------------------------------- 1 | import { network } from "hardhat"; 2 | import * as helpers from "@nomicfoundation/hardhat-network-helpers"; 3 | 4 | export function takeSnapshotBeforeAndAfterEveryTest(pre_req: () => Promise) { 5 | let localsnapshot: any; 6 | 7 | beforeEach(async function() { 8 | localsnapshot = await helpers.takeSnapshot(); 9 | 10 | await pre_req(); 11 | }); 12 | 13 | afterEach(async function() { 14 | await localsnapshot.restore() 15 | }); 16 | } 17 | 18 | export function saveAndRestoreStateToParent(pre_req: () => Promise) { 19 | let localsnapshot: any; 20 | 21 | before(async function() { 22 | localsnapshot = await network.provider.request({ 23 | method: "evm_snapshot", 24 | params: [], 25 | }); 26 | this.timeout(400000); 27 | 28 | await pre_req(); 29 | }); 30 | 31 | after(async function() { 32 | await network.provider.request({ 33 | method: "evm_revert", 34 | params: [localsnapshot], 35 | }); 36 | }); 37 | } 38 | -------------------------------------------------------------------------------- /scripts/interact.ts: -------------------------------------------------------------------------------- 1 | import { ethers } from "hardhat"; 2 | import { StakeManager, StakeManager__factory } from "../typechain-types"; 3 | import { getPond, getStakeManager } from "../utils/typechainConvertor"; 4 | 5 | async function main() { 6 | const signer = await ethers.getSigner("0xB38e8c17e38363aF6EbdCb3dAE12e0243582891D"); 7 | const StakeManager = getStakeManager("0xf90490186f370f324def2871f077668455f65253", signer); 8 | const Pond = getPond("0xdA0a57B710768ae17941a9Fa33f8B720c8bD9ddD", signer); 9 | 10 | await Pond.approve("0xf90490186f370f324def2871f077668455f65253", ethers.utils.parseEther("100000")); 11 | 12 | const tx = await StakeManager.createStashAndDelegate( 13 | ["0x5802add45f8ec0a524470683e7295faacc853f97cf4a8d3ffbaaf25ce0fd87c4"], 14 | [ethers.utils.parseEther("1")], 15 | "0x6017b5df98118730e4c31b17c5a7438ecaa8887b" 16 | ); 17 | 18 | const receipt = await tx.wait(); 19 | console.log(JSON.stringify(receipt, null, 2)) 20 | } 21 | 22 | main(); -------------------------------------------------------------------------------- /contracts/staking/interfaces/IClusterRewards.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import "./IClusterSelector.sol"; 6 | 7 | interface IClusterRewards { 8 | function clusterSelectors(bytes32 networkId) external returns (IClusterSelector); 9 | function clusterRewards(address cluster) external returns(uint256); 10 | function rewardWeight(bytes32 networkId) external returns(uint256); 11 | function totalRewardsPerEpoch() external returns(uint256); 12 | function addNetwork(bytes32 networkId, uint256 rewardWeight, address clusterSelector) external; 13 | function removeNetwork(bytes32 networkId) external; 14 | function updateNetwork(bytes32 networkId, uint256 updatedRewardWeight, address updatedClusterSelector) external; 15 | function getRewardForEpoch(uint256 epoch, bytes32 networkId) external view returns(uint256); 16 | function claimReward(address cluster) external returns(uint256); 17 | function changeRewardPerEpoch(uint256 updatedRewardPerEpoch) external; 18 | } 19 | -------------------------------------------------------------------------------- /deployments/utils/Upgrade.ts: -------------------------------------------------------------------------------- 1 | import { ethers, upgrades } from 'hardhat'; 2 | import * as fs from 'fs'; 3 | 4 | export async function upgrade(contractName: string, contractId: string, constructorArgs?: any[]) { 5 | let chainId = (await ethers.provider.getNetwork()).chainId; 6 | console.log("Chain Id:", chainId); 7 | 8 | var addresses: {[key: string]: {[key: string]: string}} = {}; 9 | if(fs.existsSync('address.json')) { 10 | addresses = JSON.parse(fs.readFileSync('address.json', 'utf8')); 11 | } 12 | 13 | if(addresses[chainId] === undefined || 14 | addresses[chainId][contractId] === undefined 15 | ) { 16 | console.log("Missing dependencies"); 17 | return; 18 | } 19 | 20 | let signers = await ethers.getSigners(); 21 | let addrs = await Promise.all(signers.map(a => a.getAddress())); 22 | 23 | console.log("Signer addrs:", addrs); 24 | 25 | const CF = await ethers.getContractFactory(contractName); 26 | let c = await upgrades.upgradeProxy(addresses[chainId][contractId], CF, { 27 | kind: "uups", 28 | constructorArgs 29 | }); 30 | 31 | console.log("Deployed addr:", c.address); 32 | } -------------------------------------------------------------------------------- /scripts/rbac/MPondWhitelist.ts: -------------------------------------------------------------------------------- 1 | import { ethers } from 'hardhat'; 2 | import * as fs from 'fs'; 3 | 4 | 5 | async function main() { 6 | let name = process.env.NAME || 'MPond'; 7 | let wname = process.env.WNAME || 'MPondGateway'; 8 | console.log(name); 9 | 10 | let chainId = (await ethers.provider.getNetwork()).chainId; 11 | console.log("Chain Id:", chainId); 12 | 13 | var addresses: {[key: string]: {[key: string]: string}} = {}; 14 | if(fs.existsSync('address.json')) { 15 | addresses = JSON.parse(fs.readFileSync('address.json', 'utf8')); 16 | } 17 | 18 | if(addresses[chainId] === undefined || 19 | addresses[chainId][name] === undefined || 20 | addresses[chainId][wname] === undefined 21 | ) { 22 | console.log("Missing dependencies"); 23 | return; 24 | } 25 | 26 | let signers = await ethers.getSigners(); 27 | let addrs = await Promise.all(signers.map(a => a.getAddress())); 28 | 29 | console.log("Signer addrs:", addrs); 30 | 31 | const MPond = await ethers.getContractFactory('MPond'); 32 | let mpond = MPond.attach(addresses[chainId][name]); 33 | 34 | console.log("Deployed addr:", mpond.address); 35 | 36 | await mpond.grantRole( 37 | await mpond.WHITELIST_ROLE(), 38 | addresses[chainId][wname] 39 | ); 40 | } 41 | 42 | main() 43 | .then(() => process.exit(0)) 44 | .catch((error) => { 45 | console.error(error); 46 | process.exit(1); 47 | }); 48 | 49 | -------------------------------------------------------------------------------- /scripts/deploy/Upgrade.ts: -------------------------------------------------------------------------------- 1 | import { ethers, upgrades } from 'hardhat'; 2 | import { BigNumber as BN, Signer, Contract } from 'ethers'; 3 | import * as fs from 'fs'; 4 | 5 | 6 | declare module 'ethers' { 7 | interface BigNumber { 8 | e18(this: BigNumber): BigNumber; 9 | } 10 | } 11 | BN.prototype.e18 = function () { 12 | return this.mul(BN.from(10).pow(18)) 13 | } 14 | 15 | 16 | async function main() { 17 | let name = process.env.NAME || 'Contract'; 18 | console.log(name); 19 | 20 | let chainId = (await ethers.provider.getNetwork()).chainId; 21 | console.log("Chain Id:", chainId); 22 | 23 | var addresses: {[key: string]: {[key: string]: string}} = {}; 24 | if(fs.existsSync('address.json')) { 25 | addresses = JSON.parse(fs.readFileSync('address.json', 'utf8')); 26 | } 27 | 28 | if(addresses[chainId] === undefined || 29 | addresses[chainId][name] === undefined 30 | ) { 31 | console.log("Missing dependencies"); 32 | return; 33 | } 34 | 35 | let signers = await ethers.getSigners(); 36 | let addrs = await Promise.all(signers.map(a => a.getAddress())); 37 | 38 | console.log("Signer addrs:", addrs); 39 | 40 | const CF = await ethers.getContractFactory(name); 41 | let c = await upgrades.upgradeProxy(addresses[chainId][name], CF, { kind: "uups" }); 42 | 43 | console.log("Deployed addr:", c.address); 44 | } 45 | 46 | main() 47 | .then(() => process.exit(0)) 48 | .catch((error) => { 49 | console.error(error); 50 | process.exit(1); 51 | }); 52 | 53 | 54 | -------------------------------------------------------------------------------- /contracts/staking/interfaces/IStakeManager.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | interface IStakeManager { 6 | function stashes(bytes32 _stashId) external returns(address, address); 7 | function stashIndex() external returns(uint256); 8 | function locks(bytes32 _lockId) external returns(uint256, uint256); 9 | function lockWaitTime(bytes32 _selectorId) external returns(uint256); 10 | function updateLockWaitTime(bytes32 _selector, uint256 _updatedWaitTime) external; 11 | function updateRewardDelegators(address _updatedRewardDelegator) external; 12 | function disableToken(bytes32 _tokenId) external; 13 | function createStashAndDelegate( 14 | bytes32[] calldata _tokens, 15 | uint256[] calldata _amounts, 16 | address _delegatedCluster 17 | ) external; 18 | function createStash(bytes32[] calldata _tokens, uint256[] calldata _amounts) external returns(bytes32); 19 | function delegateStash(bytes32 _stashId, address _delegatedCluster) external; 20 | function requestStashRedelegation(bytes32 _stashId, address _newCluster) external; 21 | function redelegateStash(bytes32 _stashId) external; 22 | function undelegateStash(bytes32 _stashId) external; 23 | function withdrawStash(bytes32 _stashId) external; 24 | function withdrawStash(bytes32 _stashId, bytes32[] calldata _tokens, uint256[] calldata _amounts) external; 25 | function stashes__amounts(bytes32 _stashId, bytes32 _tokenId) external view returns(uint256); 26 | } 27 | -------------------------------------------------------------------------------- /deployments/arbBridge/L2GatewayUpgrade.ts: -------------------------------------------------------------------------------- 1 | import { ethers, upgrades } from 'hardhat'; 2 | import { BigNumber as BN, Signer, Contract } from 'ethers'; 3 | import * as fs from 'fs'; 4 | 5 | 6 | declare module 'ethers' { 7 | interface BigNumber { 8 | e18(this: BigNumber): BigNumber; 9 | } 10 | } 11 | BN.prototype.e18 = function () { 12 | return this.mul(BN.from(10).pow(18)) 13 | } 14 | 15 | 16 | async function main() { 17 | let name = process.env.NAME || 'PondGateway'; 18 | console.log(name); 19 | 20 | let chainId = (await ethers.provider.getNetwork()).chainId; 21 | console.log("Chain Id:", chainId); 22 | 23 | var addresses: {[key: string]: {[key: string]: string}} = {}; 24 | if(fs.existsSync('address.json')) { 25 | addresses = JSON.parse(fs.readFileSync('address.json', 'utf8')); 26 | } 27 | 28 | if(addresses[chainId] === undefined || addresses[chainId][name] === undefined) { 29 | console.log("Missing dependencies"); 30 | return; 31 | } 32 | 33 | let signers = await ethers.getSigners(); 34 | let addrs = await Promise.all(signers.map(a => a.getAddress())); 35 | 36 | console.log("Signer addrs:", addrs); 37 | 38 | const L2Gateway = await ethers.getContractFactory('L2Gateway'); 39 | let l2Gateway = await upgrades.upgradeProxy(addresses[chainId][name], L2Gateway, { kind: "uups" }); 40 | 41 | console.log("Deployed addr:", l2Gateway.address); 42 | } 43 | 44 | main() 45 | .then(() => process.exit(0)) 46 | .catch((error) => { 47 | console.error(error); 48 | process.exit(1); 49 | }); 50 | 51 | -------------------------------------------------------------------------------- /deployments/tokens/MPondNew.ts: -------------------------------------------------------------------------------- 1 | import { ethers, upgrades } from 'hardhat'; 2 | import { BigNumber as BN } from 'ethers'; 3 | import * as fs from 'fs'; 4 | 5 | 6 | declare module 'ethers' { 7 | interface BigNumber { 8 | e18(this: BigNumber): BigNumber; 9 | } 10 | } 11 | BN.prototype.e18 = function () { 12 | return this.mul(BN.from(10).pow(18)) 13 | } 14 | 15 | 16 | async function main() { 17 | let chainId = (await ethers.provider.getNetwork()).chainId; 18 | console.log("Chain Id:", chainId); 19 | 20 | var addresses: {[key: string]: {[key: string]: string}} = {}; 21 | if(fs.existsSync('address.json')) { 22 | addresses = JSON.parse(fs.readFileSync('address.json', 'utf8')); 23 | } 24 | 25 | if(addresses[chainId] === undefined) { 26 | addresses[chainId] = {}; 27 | } 28 | 29 | if(addresses[chainId]['MPond'] !== undefined) { 30 | console.log("Existing deployment:", addresses[chainId]['MPond']); 31 | return; 32 | } 33 | 34 | let signers = await ethers.getSigners(); 35 | let addrs = await Promise.all(signers.map(a => a.getAddress())); 36 | 37 | console.log("Signer addrs:", addrs); 38 | 39 | const MPond = await ethers.getContractFactory('MPond'); 40 | let mpond = await upgrades.deployProxy(MPond, { kind: "uups" }); 41 | 42 | console.log("Deployed addr:", mpond.address); 43 | 44 | addresses[chainId]['MPond'] = mpond.address; 45 | 46 | fs.writeFileSync('address.json', JSON.stringify(addresses, null, 2), 'utf8'); 47 | } 48 | 49 | main() 50 | .then(() => process.exit(0)) 51 | .catch((error) => { 52 | console.error(error); 53 | process.exit(1); 54 | }); 55 | 56 | -------------------------------------------------------------------------------- /scripts/deploy/token/MPond.ts: -------------------------------------------------------------------------------- 1 | import { ethers, upgrades } from 'hardhat'; 2 | import { BigNumber as BN, Signer, Contract } from 'ethers'; 3 | import * as fs from 'fs'; 4 | 5 | 6 | declare module 'ethers' { 7 | interface BigNumber { 8 | e18(this: BigNumber): BigNumber; 9 | } 10 | } 11 | BN.prototype.e18 = function () { 12 | return this.mul(BN.from(10).pow(18)) 13 | } 14 | 15 | 16 | async function main() { 17 | let chainId = (await ethers.provider.getNetwork()).chainId; 18 | console.log("Chain Id:", chainId); 19 | 20 | var addresses: {[key: string]: {[key: string]: string}} = {}; 21 | if(fs.existsSync('address.json')) { 22 | addresses = JSON.parse(fs.readFileSync('address.json', 'utf8')); 23 | } 24 | 25 | if(addresses[chainId] === undefined) { 26 | addresses[chainId] = {}; 27 | } 28 | 29 | if(addresses[chainId]['MPond'] !== undefined) { 30 | console.log("Existing deployment:", addresses[chainId]['MPond']); 31 | return; 32 | } 33 | 34 | let signers = await ethers.getSigners(); 35 | let addrs = await Promise.all(signers.map(a => a.getAddress())); 36 | 37 | console.log("Signer addrs:", addrs); 38 | 39 | const MPond = await ethers.getContractFactory('MPond'); 40 | let mpond = await upgrades.deployProxy(MPond, { kind: "uups" }); 41 | 42 | console.log("Deployed addr:", mpond.address); 43 | 44 | addresses[chainId]['MPond'] = mpond.address; 45 | 46 | fs.writeFileSync('address.json', JSON.stringify(addresses, null, 2), 'utf8'); 47 | } 48 | 49 | main() 50 | .then(() => process.exit(0)) 51 | .catch((error) => { 52 | console.error(error); 53 | process.exit(1); 54 | }); 55 | 56 | 57 | -------------------------------------------------------------------------------- /deployments/tokens/PondNew.ts: -------------------------------------------------------------------------------- 1 | import { ethers, upgrades } from 'hardhat'; 2 | import { BigNumber as BN, Signer, Contract } from 'ethers'; 3 | import * as fs from 'fs'; 4 | 5 | 6 | declare module 'ethers' { 7 | interface BigNumber { 8 | e18(this: BigNumber): BigNumber; 9 | } 10 | } 11 | BN.prototype.e18 = function () { 12 | return this.mul(BN.from(10).pow(18)) 13 | } 14 | 15 | 16 | async function main() { 17 | let chainId = (await ethers.provider.getNetwork()).chainId; 18 | console.log("Chain Id:", chainId); 19 | 20 | var addresses: {[key: string]: {[key: string]: string}} = {}; 21 | if(fs.existsSync('address.json')) { 22 | addresses = JSON.parse(fs.readFileSync('address.json', 'utf8')); 23 | } 24 | 25 | if(addresses[chainId] === undefined) { 26 | addresses[chainId] = {}; 27 | } 28 | 29 | if(addresses[chainId]['Pond'] !== undefined) { 30 | console.log("Existing deployment:", addresses[chainId]['Pond']); 31 | return; 32 | } 33 | 34 | let signers = await ethers.getSigners(); 35 | let addrs = await Promise.all(signers.map(a => a.getAddress())); 36 | 37 | console.log("Signer addrs:", addrs); 38 | 39 | const Pond = await ethers.getContractFactory('Pond'); 40 | let pond = await upgrades.deployProxy(Pond, ["Marlin POND", "POND"], { kind: "uups" }); 41 | 42 | console.log("Deployed addr:", pond.address); 43 | 44 | addresses[chainId]['Pond'] = pond.address; 45 | 46 | fs.writeFileSync('address.json', JSON.stringify(addresses, null, 2), 'utf8'); 47 | } 48 | 49 | main() 50 | .then(() => process.exit(0)) 51 | .catch((error) => { 52 | console.error(error); 53 | process.exit(1); 54 | }); 55 | 56 | -------------------------------------------------------------------------------- /deployments/arbBridge/L1GatewayUpgrade.ts: -------------------------------------------------------------------------------- 1 | import { ethers, upgrades } from 'hardhat'; 2 | import { BigNumber as BN, Signer, Contract } from 'ethers'; 3 | import * as fs from 'fs'; 4 | 5 | 6 | declare module 'ethers' { 7 | interface BigNumber { 8 | e18(this: BigNumber): BigNumber; 9 | } 10 | } 11 | BN.prototype.e18 = function () { 12 | return this.mul(BN.from(10).pow(18)) 13 | } 14 | 15 | 16 | async function main() { 17 | let name = process.env.NAME || 'L1Gateway'; 18 | console.log(name); 19 | 20 | let chainId = (await ethers.provider.getNetwork()).chainId; 21 | console.log("Chain Id:", chainId); 22 | 23 | var addresses: {[key: string]: {[key: string]: string}} = {}; 24 | if(fs.existsSync('address.json')) { 25 | addresses = JSON.parse(fs.readFileSync('address.json', 'utf8')); 26 | } 27 | 28 | if(addresses[chainId] === undefined || addresses[chainId][name] === undefined) { 29 | console.log("Missing dependencies"); 30 | return; 31 | } 32 | 33 | let signers = await ethers.getSigners(); 34 | let addrs = await Promise.all(signers.map(a => a.getAddress())); 35 | 36 | console.log("Signer addrs:", addrs); 37 | 38 | const L1Gateway = await ethers.getContractFactory('L1Gateway'); 39 | let l1Gateway = await upgrades.upgradeProxy(addresses[chainId][name], L1Gateway, { kind: "uups" }); 40 | // let l1Gateway = await upgrades.upgradeProxy(addresses[chainId][name], L1Gateway, { kind: "uups", unsafeAllowRenames: true }); 41 | 42 | console.log("Deployed addr:", l1Gateway.address); 43 | } 44 | 45 | main() 46 | .then(() => process.exit(0)) 47 | .catch((error) => { 48 | console.error(error); 49 | process.exit(1); 50 | }); 51 | 52 | -------------------------------------------------------------------------------- /scripts/deploy/enclaves/call.ts: -------------------------------------------------------------------------------- 1 | import { ethers } from "hardhat"; 2 | 3 | import { IERC20Upgradeable__factory } from "../../../typechain-types"; 4 | import { getConfig } from "../../helper/helper"; 5 | 6 | const SEND_TO = "0x47d40316867853189E1e04dc1eb53Dc71C8eb946"; 7 | 8 | async function main() { 9 | const { addresses, signers } = await getConfig(); 10 | 11 | const admin = signers[0]; 12 | console.log(admin); 13 | 14 | const credit = await ethers.getContractAt("Credit", addresses.proxy.credit); 15 | 16 | // // grant `MINTER_ROLE` to admin 17 | // const minter_tx = await credit.connect(admin).grantRole(await credit.MINTER_ROLE(), admin.address); 18 | // console.log(minter_tx); 19 | 20 | // // grant `TRANSFER_ALLOWED_ROLE` to `SEND_TO` 21 | // const transfer_allowed_tx = await credit.connect(admin).grantRole(await credit.TRANSFER_ALLOWED_ROLE(), SEND_TO); 22 | // console.log(transfer_allowed_tx); 23 | 24 | // // mint 500 credits to `SEND_TO` 25 | // const credit_mint_tx = await credit.connect(admin).mint(SEND_TO, ethers.utils.parseUnits("500", 6)); 26 | // console.log(credit_mint_tx); 27 | 28 | // // const usdc = AccessControlEnumerableUpgradeable__factory.connect(addresses.proxy.usdc, admin); 29 | 30 | // transfer 1000 usdc to credit 31 | const usdc = IERC20Upgradeable__factory.connect(addresses.proxy.usdc, admin); 32 | await usdc.transfer(addresses.proxy.credit, ethers.utils.parseUnits("1000", 6)); 33 | 34 | // grant `TRANSFER_ALLOWED_ROLE` to address(0) 35 | const transfer_allowed_tx = await credit.connect(admin).grantRole(await credit.TRANSFER_ALLOWED_ROLE(), addresses.proxy.marketV1); 36 | console.log(transfer_allowed_tx); 37 | } 38 | 39 | main(); -------------------------------------------------------------------------------- /contracts/token/IArbToken.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | /* 4 | * Copyright 2020, Offchain Labs, Inc. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | /** 20 | * @title Minimum expected interface for L2 token that interacts with the L2 token bridge (this is the interface necessary 21 | * for a custom token that interacts with the bridge, see TestArbCustomToken.sol for an example implementation). 22 | */ 23 | 24 | // solhint-disable-next-line compiler-version 25 | pragma solidity >=0.6.9 <0.9.0; 26 | 27 | interface IArbToken { 28 | /** 29 | * @notice should increase token supply by amount, and should (probably) only be callable by the L1 bridge. 30 | */ 31 | function bridgeMint(address account, uint256 amount) external; 32 | 33 | /** 34 | * @notice should decrease token supply by amount, and should (probably) only be callable by the L1 bridge. 35 | */ 36 | function bridgeBurn(address account, uint256 amount) external; 37 | 38 | /** 39 | * @return address of layer 1 token 40 | */ 41 | function l1Address() external view returns (address); 42 | } 43 | 44 | -------------------------------------------------------------------------------- /scripts/deploy/token/Pond.ts: -------------------------------------------------------------------------------- 1 | import { ethers, upgrades, network } from 'hardhat'; 2 | import { BigNumber as BN, Signer, Contract } from 'ethers'; 3 | import * as fs from 'fs'; 4 | 5 | 6 | declare module 'ethers' { 7 | interface BigNumber { 8 | e18(this: BigNumber): BigNumber; 9 | } 10 | } 11 | BN.prototype.e18 = function () { 12 | return this.mul(BN.from(10).pow(18)) 13 | } 14 | 15 | 16 | async function main() { 17 | let chainId = (network.config as any).tag || (await ethers.provider.getNetwork()).chainId; 18 | console.log("Chain Id:", chainId); 19 | 20 | var addresses: {[key: string]: {[key: string]: string}} = {}; 21 | if(fs.existsSync('address.json')) { 22 | addresses = JSON.parse(fs.readFileSync('address.json', 'utf8')); 23 | } 24 | 25 | if(addresses[chainId] === undefined) { 26 | addresses[chainId] = {}; 27 | } 28 | 29 | if(addresses[chainId]['Pond'] !== undefined) { 30 | console.log("Existing deployment:", addresses[chainId]['Pond']); 31 | return; 32 | } 33 | 34 | let signers = await ethers.getSigners(); 35 | let addrs = await Promise.all(signers.map(a => a.getAddress())); 36 | 37 | console.log("Signer addrs:", addrs); 38 | 39 | const Pond = await ethers.getContractFactory('Pond'); 40 | let pond = await upgrades.deployProxy(Pond, ["Marlin POND", "POND"], { kind: "uups" }); 41 | 42 | console.log("Deployed addr:", pond.address); 43 | 44 | addresses[chainId]['Pond'] = pond.address; 45 | 46 | fs.writeFileSync('address.json', JSON.stringify(addresses, null, 2), 'utf8'); 47 | } 48 | 49 | main() 50 | .then(() => process.exit(0)) 51 | .catch((error) => { 52 | console.error(error); 53 | process.exit(1); 54 | }); 55 | 56 | 57 | -------------------------------------------------------------------------------- /contracts/staking/interfaces/IClusterRegistry.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | interface IClusterRegistry { 6 | function locks(bytes32 _lockId) external returns(uint256, uint256); 7 | function lockWaitTime(bytes32 _selectorId) external returns(uint256); 8 | function updateLockWaitTime(bytes32 _selector, uint256 _updatedWaitTime) external; 9 | function register( 10 | bytes32 _networkId, 11 | uint256 _commission, 12 | address _rewardAddress, 13 | address _clientKey 14 | ) external; 15 | function requestCommissionUpdate(uint256 _commission) external; 16 | function updateCommission() external; 17 | function requestNetworkSwitch(bytes32 _networkId) external; 18 | function switchNetwork() external; 19 | function updateRewardAddress(address _rewardAddress) external; 20 | function updateClientKey(address _clientKey) external; 21 | function requestUnregister() external; 22 | function unregister() external; 23 | function isClusterValid(address _cluster) external returns(bool); 24 | function getCommission(address _cluster) external returns(uint256); 25 | function getNetwork(address _cluster) external returns(bytes32); 26 | function getRewardAddress(address _cluster) external view returns(address); 27 | function getClientKey(address _cluster) external view returns(address); 28 | function getCluster(address _cluster) external returns( 29 | uint256 commission, 30 | address rewardAddress, 31 | address clientKey, 32 | bytes32 networkId, 33 | bool isValidCluster 34 | ); 35 | function getRewardInfo(address _cluster) external returns(uint256, address); 36 | } 37 | -------------------------------------------------------------------------------- /deployments/arbBridge/L1GatewayDeploy.ts: -------------------------------------------------------------------------------- 1 | import { ethers, upgrades } from 'hardhat'; 2 | import { BigNumber as BN, Signer, Contract } from 'ethers'; 3 | import * as fs from 'fs'; 4 | 5 | 6 | declare module 'ethers' { 7 | interface BigNumber { 8 | e18(this: BigNumber): BigNumber; 9 | } 10 | } 11 | BN.prototype.e18 = function () { 12 | return this.mul(BN.from(10).pow(18)) 13 | } 14 | 15 | 16 | async function main() { 17 | let name = process.env.NAME || 'L1Gateway'; 18 | console.log(name); 19 | 20 | let chainId = (await ethers.provider.getNetwork()).chainId; 21 | console.log("Chain Id:", chainId); 22 | 23 | var addresses: {[key: string]: {[key: string]: string}} = {}; 24 | if(fs.existsSync('address.json')) { 25 | addresses = JSON.parse(fs.readFileSync('address.json', 'utf8')); 26 | } 27 | 28 | if(addresses[chainId] === undefined) { 29 | addresses[chainId] = {}; 30 | } 31 | 32 | if(addresses[chainId][name] !== undefined) { 33 | console.log("Existing deployment:", addresses[chainId][name]); 34 | return; 35 | } 36 | 37 | let signers = await ethers.getSigners(); 38 | let addrs = await Promise.all(signers.map(a => a.getAddress())); 39 | 40 | console.log("Signer addrs:", addrs); 41 | 42 | const L1Gateway = await ethers.getContractFactory('L1Gateway'); 43 | let l1Gateway = await upgrades.deployProxy(L1Gateway, { kind: "uups", initializer: false }); 44 | 45 | console.log("Deployed addr:", l1Gateway.address); 46 | 47 | addresses[chainId][name] = l1Gateway.address; 48 | 49 | fs.writeFileSync('address.json', JSON.stringify(addresses, null, 2), 'utf8'); 50 | } 51 | 52 | main() 53 | .then(() => process.exit(0)) 54 | .catch((error) => { 55 | console.error(error); 56 | process.exit(1); 57 | }); 58 | 59 | -------------------------------------------------------------------------------- /deployments/arbBridge/L2GatewayDeploy.ts: -------------------------------------------------------------------------------- 1 | import { ethers, upgrades } from 'hardhat'; 2 | import { BigNumber as BN, Signer, Contract } from 'ethers'; 3 | import * as fs from 'fs'; 4 | 5 | 6 | declare module 'ethers' { 7 | interface BigNumber { 8 | e18(this: BigNumber): BigNumber; 9 | } 10 | } 11 | BN.prototype.e18 = function () { 12 | return this.mul(BN.from(10).pow(18)) 13 | } 14 | 15 | 16 | async function main() { 17 | let name = process.env.NAME || 'L2Gateway'; 18 | console.log(name); 19 | 20 | let chainId = (await ethers.provider.getNetwork()).chainId; 21 | console.log("Chain Id:", chainId); 22 | 23 | var addresses: {[key: string]: {[key: string]: string}} = {}; 24 | if(fs.existsSync('address.json')) { 25 | addresses = JSON.parse(fs.readFileSync('address.json', 'utf8')); 26 | } 27 | 28 | if(addresses[chainId] === undefined) { 29 | addresses[chainId] = {}; 30 | } 31 | 32 | if(addresses[chainId][name] !== undefined) { 33 | console.log("Existing deployment:", addresses[chainId][name]); 34 | return; 35 | } 36 | 37 | let signers = await ethers.getSigners(); 38 | let addrs = await Promise.all(signers.map(a => a.getAddress())); 39 | 40 | console.log("Signer addrs:", addrs); 41 | 42 | const L2Gateway = await ethers.getContractFactory('L2Gateway'); 43 | let l2Gateway = await upgrades.deployProxy(L2Gateway, { kind: "uups", initializer: false }); 44 | 45 | console.log("Deployed addr:", l2Gateway.address); 46 | 47 | addresses[chainId][name] = l2Gateway.address; 48 | 49 | fs.writeFileSync('address.json', JSON.stringify(addresses, null, 2), 'utf8'); 50 | } 51 | 52 | main() 53 | .then(() => process.exit(0)) 54 | .catch((error) => { 55 | console.error(error); 56 | process.exit(1); 57 | }); 58 | 59 | -------------------------------------------------------------------------------- /scripts/deploy/staking/ClusterRegistry.ts: -------------------------------------------------------------------------------- 1 | import { ethers, upgrades } from 'hardhat'; 2 | import { BigNumber as BN, Signer, Contract } from 'ethers'; 3 | import * as fs from 'fs'; 4 | 5 | 6 | declare module 'ethers' { 7 | interface BigNumber { 8 | e18(this: BigNumber): BigNumber; 9 | } 10 | } 11 | BN.prototype.e18 = function () { 12 | return this.mul(BN.from(10).pow(18)) 13 | } 14 | 15 | 16 | async function main() { 17 | let chainId = (await ethers.provider.getNetwork()).chainId; 18 | console.log("Chain Id:", chainId); 19 | 20 | var addresses: {[key: string]: {[key: string]: string}} = {}; 21 | if(fs.existsSync('address.json')) { 22 | addresses = JSON.parse(fs.readFileSync('address.json', 'utf8')); 23 | } 24 | 25 | if(addresses[chainId] === undefined) { 26 | addresses[chainId] = {}; 27 | } 28 | 29 | if(addresses[chainId]['ClusterRegistry'] !== undefined) { 30 | console.log("Existing deployment:", addresses[chainId]['ClusterRegistry']); 31 | return; 32 | } 33 | 34 | let signers = await ethers.getSigners(); 35 | let addrs = await Promise.all(signers.map(a => a.getAddress())); 36 | 37 | console.log("Signer addrs:", addrs); 38 | 39 | const ClusterRegistry = await ethers.getContractFactory('ClusterRegistry'); 40 | let clusterRegistry = await upgrades.deployProxy(ClusterRegistry, [[180, 240, 300]], { kind: "uups" }); 41 | 42 | console.log("Deployed addr:", clusterRegistry.address); 43 | 44 | addresses[chainId]['ClusterRegistry'] = clusterRegistry.address; 45 | 46 | fs.writeFileSync('address.json', JSON.stringify(addresses, null, 2), 'utf8'); 47 | } 48 | 49 | main() 50 | .then(() => process.exit(0)) 51 | .catch((error) => { 52 | console.error(error); 53 | process.exit(1); 54 | }); 55 | 56 | 57 | -------------------------------------------------------------------------------- /scripts/deploy/staking/RewardDelegators.ts: -------------------------------------------------------------------------------- 1 | import { ethers, upgrades } from 'hardhat'; 2 | import { BigNumber as BN, Signer, Contract } from 'ethers'; 3 | import * as fs from 'fs'; 4 | 5 | 6 | declare module 'ethers' { 7 | interface BigNumber { 8 | e18(this: BigNumber): BigNumber; 9 | } 10 | } 11 | BN.prototype.e18 = function () { 12 | return this.mul(BN.from(10).pow(18)) 13 | } 14 | 15 | 16 | async function main() { 17 | let chainId = (await ethers.provider.getNetwork()).chainId; 18 | console.log("Chain Id:", chainId); 19 | 20 | var addresses: {[key: string]: {[key: string]: string}} = {}; 21 | if(fs.existsSync('address.json')) { 22 | addresses = JSON.parse(fs.readFileSync('address.json', 'utf8')); 23 | } 24 | 25 | if(addresses[chainId] === undefined) { 26 | addresses[chainId] = {}; 27 | } 28 | 29 | if(addresses[chainId]['RewardDelegators'] !== undefined) { 30 | console.log("Existing deployment:", addresses[chainId]['RewardDelegators']); 31 | return; 32 | } 33 | 34 | let signers = await ethers.getSigners(); 35 | let addrs = await Promise.all(signers.map(a => a.getAddress())); 36 | 37 | console.log("Signer addrs:", addrs); 38 | 39 | const RewardDelegators = await ethers.getContractFactory('RewardDelegators'); 40 | let rewardDelegators = await upgrades.deployProxy(RewardDelegators, { kind: "uups", initializer: false }); 41 | 42 | console.log("Deployed addr:", rewardDelegators.address); 43 | 44 | addresses[chainId]['RewardDelegators'] = rewardDelegators.address; 45 | 46 | fs.writeFileSync('address.json', JSON.stringify(addresses, null, 2), 'utf8'); 47 | } 48 | 49 | main() 50 | .then(() => process.exit(0)) 51 | .catch((error) => { 52 | console.error(error); 53 | process.exit(1); 54 | }); 55 | 56 | 57 | -------------------------------------------------------------------------------- /deployments/enclaves/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "137": { 3 | "enclaves": { 4 | "whitelistedImages": [ 5 | { 6 | "PCR": { 7 | "PCR0": "17bf8f048519797be90497001a7559a3d555395937117d76f8baaedf56ca6d97952de79479bc0c76e5d176d20f663790", 8 | "PCR1": "5d3938eb05288e20a981038b1861062ff4174884968a39aee5982b312894e60561883576cc7381d1a7d05b809936bd16", 9 | "PCR2": "249037310daa90f4eb0703e7b105a241fdb09f12d3c40b7aea2f39e57b07b887e6ff4e4f9757943e127e391626b5e4d5" 10 | }, 11 | "enclaveKey": "0x54Be6e215F194645AFF49B348f303a7810881De9" 12 | } 13 | ], 14 | "paymentToken": "0x3c499c542cef5e3811e1192ce70d8cc03d5c3359", 15 | "lockWaitTimes": [{ 16 | "selector": "0xbb00c34f1a27e23493f1bd516e88ec0af9b091cc990f207e765f5bd5af012243", 17 | "time": 300 18 | }] 19 | }, 20 | "admin": "0x31CeB460C8085CD5324e90966ea718Ed7CBc09c6" 21 | }, 22 | "421614": { 23 | "enclaves": { 24 | "whitelistedImages": [ 25 | { 26 | "PCR": { 27 | "PCR0": "cbc96d11393fc4c7209f0e802f73dfd4cc7620f38c6c05884e13f41852f2a7fdbe516e351632dc16eb6e26b55b2593eb", 28 | "PCR1": "5d3938eb05288e20a981038b1861062ff4174884968a39aee5982b312894e60561883576cc7381d1a7d05b809936bd16", 29 | "PCR2": "d58c90473be606c14b940bcea5694ac765a914a08038f216e485b5339cb609706f80838771d93b47c7944643097bef09" 30 | }, 31 | "enclaveKey": "0x62f3392bcbc10f5ec56355d616eb598e5c5ca782" 32 | } 33 | ], 34 | "paymentToken": "Pond", 35 | "lockWaitTimes": [{ 36 | "selector": "0xbb00c34f1a27e23493f1bd516e88ec0af9b091cc990f207e765f5bd5af012243", 37 | "time": 300 38 | }] 39 | }, 40 | "admin": "0xd7E109d2219b5b5b90656FB8B33F2ba679b22062" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /scripts/erc20/Transfer.ts: -------------------------------------------------------------------------------- 1 | import { ethers, upgrades } from 'hardhat'; 2 | import { BigNumber as BN, Signer, Contract } from 'ethers'; 3 | import * as fs from 'fs'; 4 | 5 | 6 | declare module 'ethers' { 7 | interface BigNumber { 8 | e18(this: BigNumber): BigNumber; 9 | } 10 | } 11 | BN.prototype.e18 = function () { 12 | return this.mul(BN.from(10).pow(18)) 13 | } 14 | 15 | 16 | async function main() { 17 | let name = process.env.NAME || 'Token'; 18 | let address = process.env.TO; 19 | let wname = process.env.WNAME || 'TokenGateway'; 20 | let amount = BN.from(process.env.AMOUNT || 1).e18(); 21 | console.log(name, wname, amount); 22 | 23 | let chainId = (await ethers.provider.getNetwork()).chainId; 24 | console.log("Chain Id:", chainId); 25 | 26 | var addresses: {[key: string]: {[key: string]: string}} = {}; 27 | if(fs.existsSync('address.json')) { 28 | addresses = JSON.parse(fs.readFileSync('address.json', 'utf8')); 29 | } 30 | 31 | if(addresses[chainId] === undefined || 32 | addresses[chainId][name] === undefined || 33 | (address === undefined && addresses[chainId][wname] === undefined) 34 | ) { 35 | console.log("Missing dependencies"); 36 | return; 37 | } 38 | 39 | let signers = await ethers.getSigners(); 40 | let addrs = await Promise.all(signers.map(a => a.getAddress())); 41 | 42 | console.log("Signer addrs:", addrs); 43 | 44 | const Pond = await ethers.getContractFactory('Pond'); 45 | let mpond = Pond.attach(addresses[chainId][name]); 46 | 47 | console.log("Deployed addr:", mpond.address); 48 | 49 | await mpond.transfer( 50 | address || addresses[chainId][wname], 51 | amount 52 | ); 53 | } 54 | 55 | main() 56 | .then(() => process.exit(0)) 57 | .catch((error) => { 58 | console.error(error); 59 | process.exit(1); 60 | }); 61 | 62 | -------------------------------------------------------------------------------- /benchmarks/helpers/util.ts: -------------------------------------------------------------------------------- 1 | import { ethers } from "hardhat"; 2 | import { BigNumber, constants, utils } from "ethers"; 3 | 4 | // pick Y numbers less than X 5 | export async function pickYLtX(y: number, x: number, callback): Promise { 6 | const results: Promise[] = []; 7 | const picked: number[] = []; 8 | 9 | while(picked.length < y) { 10 | const index = Math.floor(Math.random() * x); 11 | if(!picked.includes(index)) { 12 | picked.push(index) 13 | results.push(callback(index)); 14 | }; 15 | } 16 | 17 | return await Promise.all(results); 18 | } 19 | 20 | export function randomlyDivideInXPieces(amount: BigNumber, X: number): BigNumber[] { 21 | let total: BigNumber = constants.Zero; 22 | let pieces: BigNumber[] = []; 23 | 24 | for(let i=0; i < X-1; i++) { 25 | pieces[i] = getRandomNumber(amount.div(X)); 26 | total = total.add(pieces[i]); 27 | } 28 | pieces[X - 1] = amount.sub(total); 29 | 30 | return pieces; 31 | } 32 | 33 | export function getRandomNumber(max: BigNumber = constants.MaxUint256, maxLoss: number = 10): BigNumber { 34 | const min = max.mul(100 - maxLoss).div(100); 35 | const rand = BigNumber.from(utils.randomBytes(32)).mod(max.sub(min)).add(min); 36 | return rand; 37 | } 38 | 39 | export async function skipBlocks(n: number) { 40 | await Promise.all([...Array(n)].map(async (x) => await ethers.provider.send("evm_mine", []))); 41 | } 42 | 43 | export async function skipTime(t: number) { 44 | await ethers.provider.send("evm_increaseTime", [t]); 45 | await skipBlocks(1); 46 | } 47 | 48 | export const gasConsumedInYear = (gasEstimate: BigNumber, l1GasInL2: BigNumber): BigNumber => { 49 | return gasEstimate.add(l1GasInL2).mul(1600).mul(50).mul(365).div(BigNumber.from(10).pow(10)) 50 | } -------------------------------------------------------------------------------- /scripts/rbac/GrantRole.ts: -------------------------------------------------------------------------------- 1 | import { ethers, upgrades } from 'hardhat'; 2 | import { BigNumber as BN, Signer, Contract } from 'ethers'; 3 | import * as fs from 'fs'; 4 | 5 | 6 | declare module 'ethers' { 7 | interface BigNumber { 8 | e18(this: BigNumber): BigNumber; 9 | } 10 | } 11 | BN.prototype.e18 = function () { 12 | return this.mul(BN.from(10).pow(18)) 13 | } 14 | 15 | 16 | async function main() { 17 | let name = process.env.NAME || 'Contract'; 18 | let cname = process.env.CNAME || name; 19 | let addr = process.env.ADDR || '0x00'; 20 | let roleString = process.env.ROLE || 'SOME_ROLE'; 21 | let role = roleString !== 'DEFAULT_ADMIN_ROLE' ? ethers.utils.id(roleString) : '0x0000000000000000000000000000000000000000000000000000000000000000'; 22 | console.log(name, cname, addr, role); 23 | 24 | let chainId = (await ethers.provider.getNetwork()).chainId; 25 | console.log("Chain Id:", chainId); 26 | 27 | var addresses: {[key: string]: {[key: string]: string}} = {}; 28 | if(fs.existsSync('address.json')) { 29 | addresses = JSON.parse(fs.readFileSync('address.json', 'utf8')); 30 | } 31 | 32 | if(addresses[chainId] === undefined || 33 | addresses[chainId][name] === undefined 34 | ) { 35 | console.log("Missing dependencies"); 36 | return; 37 | } 38 | 39 | let signers = await ethers.getSigners(); 40 | let addrs = await Promise.all(signers.map(a => a.getAddress())); 41 | 42 | console.log("Signer addrs:", addrs); 43 | 44 | const CF = await ethers.getContractFactory(cname); 45 | let c = CF.attach(addresses[chainId][name]); 46 | 47 | let res = await c.grantRole(role, addr); 48 | console.log(res); 49 | 50 | res = await res.wait(); 51 | console.log(res); 52 | 53 | } 54 | 55 | main() 56 | .then(() => process.exit(0)) 57 | .catch((error) => { 58 | console.error(error); 59 | process.exit(1); 60 | }); 61 | 62 | 63 | -------------------------------------------------------------------------------- /scripts/rbac/RevokeRole.ts: -------------------------------------------------------------------------------- 1 | import { ethers, upgrades } from 'hardhat'; 2 | import { BigNumber as BN, Signer, Contract } from 'ethers'; 3 | import * as fs from 'fs'; 4 | 5 | 6 | declare module 'ethers' { 7 | interface BigNumber { 8 | e18(this: BigNumber): BigNumber; 9 | } 10 | } 11 | BN.prototype.e18 = function () { 12 | return this.mul(BN.from(10).pow(18)) 13 | } 14 | 15 | 16 | async function main() { 17 | let name = process.env.NAME || 'Contract'; 18 | let cname = process.env.CNAME || name; 19 | let addr = process.env.ADDR || '0x00'; 20 | let roleString = process.env.ROLE || 'SOME_ROLE'; 21 | let role = roleString !== 'DEFAULT_ADMIN_ROLE' ? ethers.utils.id(roleString) : '0x0000000000000000000000000000000000000000000000000000000000000000'; 22 | console.log(name, cname, addr, role); 23 | 24 | let chainId = (await ethers.provider.getNetwork()).chainId; 25 | console.log("Chain Id:", chainId); 26 | 27 | var addresses: {[key: string]: {[key: string]: string}} = {}; 28 | if(fs.existsSync('address.json')) { 29 | addresses = JSON.parse(fs.readFileSync('address.json', 'utf8')); 30 | } 31 | 32 | if(addresses[chainId] === undefined || 33 | addresses[chainId][name] === undefined 34 | ) { 35 | console.log("Missing dependencies"); 36 | return; 37 | } 38 | 39 | let signers = await ethers.getSigners(); 40 | let addrs = await Promise.all(signers.map(a => a.getAddress())); 41 | 42 | console.log("Signer addrs:", addrs); 43 | 44 | const CF = await ethers.getContractFactory(cname); 45 | let c = CF.attach(addresses[chainId][name]); 46 | 47 | let res = await c.revokeRole(role, addr); 48 | console.log(res); 49 | 50 | res = await res.wait(); 51 | console.log(res); 52 | 53 | } 54 | 55 | main() 56 | .then(() => process.exit(0)) 57 | .catch((error) => { 58 | console.error(error); 59 | process.exit(1); 60 | }); 61 | 62 | 63 | -------------------------------------------------------------------------------- /deployments/staking/deployStaking.ts: -------------------------------------------------------------------------------- 1 | import { Contract } from 'ethers'; 2 | import { ethers } from 'hardhat'; 3 | import { deploy as deployClusterRegistry } from './ClusterRegistry'; 4 | import { deploy as deployClusterRewards } from './ClusterRewards'; 5 | import { deploy as deployClusterSelector } from './ClusterSelector'; 6 | import { deploy as deployReceiverStaking } from './ReceiverStaking'; 7 | import { deployNoInit as deployRewardDelegators } from './RewardDelegators'; 8 | import { init as initRewardDelegators } from './RewardDelegatorsInit'; 9 | import { deploy as deployStakeManager } from './StakeManager'; 10 | 11 | const config = require('./config'); 12 | 13 | async function deployStaking() { 14 | let chainId = (await ethers.provider.getNetwork()).chainId; 15 | console.log("Chain Id:", chainId); 16 | 17 | const chainConfig = config[chainId]; 18 | 19 | const rewardDelegators: Contract = await deployRewardDelegators(); 20 | const stakeManager: Contract = await deployStakeManager(rewardDelegators.address); 21 | const clusterRegistry: Contract = await deployClusterRegistry(rewardDelegators.address); 22 | const clusterSelectorMap = Object(); 23 | for(let network in chainConfig.staking.rewardWeights) { 24 | const clusterSelector = await deployClusterSelector(network, rewardDelegators.address); 25 | clusterSelectorMap[network] = clusterSelector.address; 26 | } 27 | const receiverStaking: Contract = await deployReceiverStaking(); 28 | const clusterRewards: Contract = await deployClusterRewards(rewardDelegators.address, receiverStaking.address, clusterSelectorMap); 29 | await initRewardDelegators(rewardDelegators.address, stakeManager.address, clusterRewards.address, clusterRegistry.address); 30 | } 31 | 32 | deployStaking() 33 | .then(() => process.exit(0)) 34 | .catch((error) => { 35 | console.error(error); 36 | process.exit(1); 37 | }); 38 | -------------------------------------------------------------------------------- /deployments/arbBridge/L2GatewayInit.ts: -------------------------------------------------------------------------------- 1 | import { ethers, upgrades } from 'hardhat'; 2 | import { BigNumber as BN, Signer, Contract } from 'ethers'; 3 | import * as fs from 'fs'; 4 | 5 | 6 | declare module 'ethers' { 7 | interface BigNumber { 8 | e18(this: BigNumber): BigNumber; 9 | } 10 | } 11 | BN.prototype.e18 = function () { 12 | return this.mul(BN.from(10).pow(18)) 13 | } 14 | 15 | 16 | async function main() { 17 | let name = process.env.NAME || 'L2Gateway'; 18 | let tokenName = process.env.TOKENNAME || 'Token'; 19 | let l1GName = process.env.L1GNAME || 'L1Gateway'; 20 | console.log(name); 21 | 22 | let chainId = (await ethers.provider.getNetwork()).chainId; 23 | let ethChainId = { '421611': '4', '42161': '1' }[chainId]!; 24 | console.log("Chain Id:", chainId, ethChainId); 25 | 26 | var addresses: {[key: string]: {[key: string]: string}} = {}; 27 | if(fs.existsSync('address.json')) { 28 | addresses = JSON.parse(fs.readFileSync('address.json', 'utf8')); 29 | } 30 | 31 | if(addresses[chainId] === undefined || 32 | addresses[ethChainId] === undefined || 33 | addresses[chainId][tokenName] === undefined || 34 | addresses[ethChainId][l1GName] === undefined 35 | ) { 36 | console.log("Missing dependencies"); 37 | return; 38 | } 39 | 40 | let signers = await ethers.getSigners(); 41 | let addrs = await Promise.all(signers.map(a => a.getAddress())); 42 | 43 | console.log("Signer addrs:", addrs); 44 | 45 | const L2Gateway = await ethers.getContractFactory('L2Gateway'); 46 | let l2Gateway = L2Gateway.attach(addresses[chainId][name]); 47 | 48 | console.log("Deployed addr:", l2Gateway.address); 49 | 50 | await l2Gateway.initialize( 51 | addresses[chainId][tokenName], 52 | addresses[ethChainId][l1GName] 53 | ); 54 | } 55 | 56 | main() 57 | .then(() => process.exit(0)) 58 | .catch((error) => { 59 | console.error(error); 60 | process.exit(1); 61 | }); 62 | 63 | -------------------------------------------------------------------------------- /test/helpers/common.ts: -------------------------------------------------------------------------------- 1 | import { BigNumber as BN } from "ethers"; 2 | import { ethers as ethersForType } from "hardhat"; 3 | 4 | export async function skipBlocks(ethers: typeof ethersForType, n: number) { 5 | await Promise.all([...Array(n)].map(async (x) => await ethers.provider.send("evm_mine", []))); 6 | } 7 | 8 | export async function skipTime(ethers: typeof ethersForType, t: number) { 9 | await ethers.provider.send("evm_increaseTime", [t]); 10 | await skipBlocks(ethers, 1); 11 | } 12 | 13 | export async function skipToTimestamp(ethers: typeof ethersForType, t: number) { 14 | await ethers.provider.send("evm_mine", [t]); 15 | } 16 | 17 | export async function impersonate(ethers: typeof ethersForType, account: string) { 18 | await ethers.provider.send("hardhat_impersonateAccount", [account]); 19 | return ethers.getSigner(account); 20 | } 21 | 22 | export async function setBalance(ethers: typeof ethersForType, account: string, balance: BN) { 23 | await ethers.provider.send("hardhat_setBalance", [account, balance.toHexString().replace("0x0", "0x")]); 24 | } 25 | 26 | export async function increaseBalance(ethers: typeof ethersForType, account: string, amount: BN) { 27 | const balance = await ethers.provider.getBalance(account); 28 | await ethers.provider.send("hardhat_setBalance", [account, balance.add(amount).toHexString().replace("0x0", "0x")]); 29 | } 30 | 31 | export const random = (min: BN | string, max: BN | string): string => { 32 | const randomizer = ethersForType.BigNumber.from(ethersForType.utils.randomBytes(32)); 33 | return randomizer.mod(BN.from(max).sub(min)).add(min).toString(); 34 | }; 35 | 36 | export const getRandomElementsFromArray = (array: T[], noOfElements: number): T[] => { 37 | if (array.length < noOfElements) { 38 | throw Error("Insuff Elements in array"); 39 | } 40 | 41 | return array.sort(() => 0.5 - Math.random()).slice(0, noOfElements); 42 | }; 43 | 44 | export const BIG_ZERO = BN.from(0); 45 | -------------------------------------------------------------------------------- /scripts/deploy/token/Bridge.ts: -------------------------------------------------------------------------------- 1 | import { ethers, upgrades } from 'hardhat'; 2 | import { BigNumber as BN, Signer, Contract } from 'ethers'; 3 | import * as fs from 'fs'; 4 | 5 | 6 | declare module 'ethers' { 7 | interface BigNumber { 8 | e18(this: BigNumber): BigNumber; 9 | } 10 | } 11 | BN.prototype.e18 = function () { 12 | return this.mul(BN.from(10).pow(18)) 13 | } 14 | 15 | 16 | async function main() { 17 | let chainId = (await ethers.provider.getNetwork()).chainId; 18 | console.log("Chain Id:", chainId); 19 | 20 | var addresses: {[key: string]: {[key: string]: string}} = {}; 21 | if(fs.existsSync('address.json')) { 22 | addresses = JSON.parse(fs.readFileSync('address.json', 'utf8')); 23 | } 24 | 25 | if(addresses[chainId] === undefined || 26 | addresses[chainId]['Pond'] === undefined || 27 | addresses[chainId]['MPond'] === undefined || 28 | addresses[chainId]['StakeManager'] === undefined 29 | ) { 30 | console.log("Missing dependencies"); 31 | return; 32 | } 33 | 34 | if(addresses[chainId]['Bridge'] !== undefined) { 35 | console.log("Existing deployment:", addresses[chainId]['Bridge']); 36 | return; 37 | } 38 | 39 | let signers = await ethers.getSigners(); 40 | let addrs = await Promise.all(signers.map(a => a.getAddress())); 41 | 42 | console.log("Signer addrs:", addrs); 43 | 44 | const Bridge = await ethers.getContractFactory('Bridge'); 45 | let bridge = await upgrades.deployProxy(Bridge, [ 46 | addresses[chainId]['MPond'], 47 | addresses[chainId]['Pond'], 48 | addresses[chainId]['StakeManager'] 49 | ], { kind: "uups" }); 50 | 51 | console.log("Deployed addr:", bridge.address); 52 | 53 | addresses[chainId]['Bridge'] = bridge.address; 54 | 55 | fs.writeFileSync('address.json', JSON.stringify(addresses, null, 2), 'utf8'); 56 | } 57 | 58 | main() 59 | .then(() => process.exit(0)) 60 | .catch((error) => { 61 | console.error(error); 62 | process.exit(1); 63 | }); 64 | 65 | -------------------------------------------------------------------------------- /scripts/deploy/enclaves/Credit.ts: -------------------------------------------------------------------------------- 1 | import { ethers, upgrades, network } from 'hardhat'; 2 | import { BigNumber as BN, Signer, Contract } from 'ethers'; 3 | import * as fs from 'fs'; 4 | 5 | 6 | declare module 'ethers' { 7 | interface BigNumber { 8 | e18(this: BigNumber): BigNumber; 9 | } 10 | } 11 | BN.prototype.e18 = function () { 12 | return this.mul(BN.from(10).pow(18)) 13 | } 14 | 15 | async function main() { 16 | let name = process.env.NAME || 'MarketV1'; 17 | console.log(name); 18 | 19 | let chainId = (network.config as any).tag || (await ethers.provider.getNetwork()).chainId; 20 | console.log("Chain Id:", chainId); 21 | 22 | var addresses: {[key: string]: {[key: string]: string}} = {}; 23 | if(fs.existsSync('address.json')) { 24 | addresses = JSON.parse(fs.readFileSync('address.json', 'utf8')); 25 | } 26 | 27 | if(addresses[chainId] === undefined || 28 | addresses[chainId]['Pond'] === undefined 29 | ) { 30 | console.log("Missing dependencies"); 31 | return; 32 | } 33 | 34 | if(addresses[chainId]['MarketV1'] !== undefined) { 35 | console.log("Existing deployment:", addresses[chainId]['MarketV1']); 36 | return; 37 | } 38 | 39 | let signers = await ethers.getSigners(); 40 | let addrs = await Promise.all(signers.map(a => a.getAddress())); 41 | 42 | console.log("Signer addrs:", addrs); 43 | 44 | const MarketV1 = await ethers.getContractFactory('MarketV1'); 45 | let marketv1 = await upgrades.deployProxy(MarketV1, [ 46 | addresses[chainId]['Pond'], 47 | [ethers.utils.id('RATE_LOCK')], 48 | [300], 49 | ], { kind: "uups" }); 50 | 51 | console.log("Deployed addr:", marketv1.address); 52 | 53 | addresses[chainId]['MarketV1'] = marketv1.address; 54 | 55 | fs.writeFileSync('address.json', JSON.stringify(addresses, null, 2), 'utf8'); 56 | } 57 | 58 | main() 59 | .then(() => process.exit(0)) 60 | .catch((error) => { 61 | console.error(error); 62 | process.exit(1); 63 | }); 64 | 65 | 66 | -------------------------------------------------------------------------------- /scripts/deploy/enclaves/MarketV1.ts: -------------------------------------------------------------------------------- 1 | import { ethers, upgrades, network } from 'hardhat'; 2 | import { BigNumber as BN, Signer, Contract } from 'ethers'; 3 | import * as fs from 'fs'; 4 | 5 | 6 | declare module 'ethers' { 7 | interface BigNumber { 8 | e18(this: BigNumber): BigNumber; 9 | } 10 | } 11 | BN.prototype.e18 = function () { 12 | return this.mul(BN.from(10).pow(18)) 13 | } 14 | 15 | 16 | async function main() { 17 | let name = process.env.NAME || 'MarketV1'; 18 | console.log(name); 19 | 20 | let chainId = (network.config as any).tag || (await ethers.provider.getNetwork()).chainId; 21 | console.log("Chain Id:", chainId); 22 | 23 | var addresses: {[key: string]: {[key: string]: string}} = {}; 24 | if(fs.existsSync('address.json')) { 25 | addresses = JSON.parse(fs.readFileSync('address.json', 'utf8')); 26 | } 27 | 28 | if(addresses[chainId] === undefined || 29 | addresses[chainId]['Pond'] === undefined 30 | ) { 31 | console.log("Missing dependencies"); 32 | return; 33 | } 34 | 35 | if(addresses[chainId]['MarketV1'] !== undefined) { 36 | console.log("Existing deployment:", addresses[chainId]['MarketV1']); 37 | return; 38 | } 39 | 40 | let signers = await ethers.getSigners(); 41 | let addrs = await Promise.all(signers.map(a => a.getAddress())); 42 | 43 | console.log("Signer addrs:", addrs); 44 | 45 | const MarketV1 = await ethers.getContractFactory('MarketV1'); 46 | let marketv1 = await upgrades.deployProxy(MarketV1, [ 47 | addresses[chainId]['Pond'], 48 | [ethers.utils.id('RATE_LOCK')], 49 | [300], 50 | ], { kind: "uups" }); 51 | 52 | console.log("Deployed addr:", marketv1.address); 53 | 54 | addresses[chainId]['MarketV1'] = marketv1.address; 55 | 56 | fs.writeFileSync('address.json', JSON.stringify(addresses, null, 2), 'utf8'); 57 | } 58 | 59 | main() 60 | .then(() => process.exit(0)) 61 | .catch((error) => { 62 | console.error(error); 63 | process.exit(1); 64 | }); 65 | 66 | 67 | -------------------------------------------------------------------------------- /contracts/staking/interfaces/IRewardDelegators.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | interface IRewardDelegators { 6 | function thresholdForSelection(bytes32 networkId) external returns(uint256); 7 | function addRewardFactor(bytes32 _tokenId, uint256 _rewardFactor) external; 8 | function removeRewardFactor(bytes32 _tokenId) external; 9 | function updateRewardFactor(bytes32 _tokenId, uint256 _updatedRewardFactor) external; 10 | function _updateRewards(address _cluster) external; 11 | function delegate( 12 | address _delegator, 13 | address _cluster, 14 | bytes32[] calldata _tokens, 15 | uint256[] calldata _amounts 16 | ) external; 17 | function undelegate( 18 | address _delegator, 19 | address _cluster, 20 | bytes32[] calldata _tokens, 21 | uint256[] calldata _amounts 22 | ) external; 23 | function withdrawRewards(address _delegator, address _cluster) external returns(uint256); 24 | function getClusterDelegation(address _cluster, bytes32 _tokenId) external view returns(uint256); 25 | function getDelegation(address _cluster, address _delegator, bytes32 _tokenId) external view returns(uint256); 26 | function updateThresholdForSelection(bytes32 networkId, uint256 thresholdForSelection) external; 27 | function updateStakeAddress(address _updatedStakeAddress) external; 28 | function updateClusterRewards(address _updatedClusterRewards) external; 29 | function updateClusterRegistry(address _updatedClusterRegistry) external; 30 | function updatePONDAddress(address _updatedPOND) external; 31 | function tokenList(uint256 index) external view returns (bytes32); 32 | function getAccRewardPerShare(address _cluster, bytes32 _tokenId) external view returns(uint256); 33 | function updateClusterDelegation(address _cluster, bytes32 _networkId) external; 34 | function removeClusterDelegation(address _cluster, bytes32 _networkId) external; 35 | } 36 | -------------------------------------------------------------------------------- /deployments/arbBridge/L1GatewayInit.ts: -------------------------------------------------------------------------------- 1 | import { ethers, upgrades } from 'hardhat'; 2 | import { BigNumber as BN, Signer, Contract } from 'ethers'; 3 | import * as fs from 'fs'; 4 | 5 | 6 | declare module 'ethers' { 7 | interface BigNumber { 8 | e18(this: BigNumber): BigNumber; 9 | } 10 | } 11 | BN.prototype.e18 = function () { 12 | return this.mul(BN.from(10).pow(18)) 13 | } 14 | 15 | 16 | async function main() { 17 | let name = process.env.NAME || 'L1Gateway'; 18 | let tokenName = process.env.TOKENNAME || 'Token'; 19 | let l2GName = process.env.L2GNAME || 'L2Gateway'; 20 | console.log(name, tokenName, l2GName); 21 | 22 | let chainId = (await ethers.provider.getNetwork()).chainId; 23 | let arbChainId = { '4': '421611', '1': '42161' }[chainId]!; 24 | console.log("Chain Id:", chainId, arbChainId); 25 | 26 | var addresses: {[key: string]: {[key: string]: string}} = {}; 27 | if(fs.existsSync('address.json')) { 28 | addresses = JSON.parse(fs.readFileSync('address.json', 'utf8')); 29 | } 30 | 31 | if(addresses[chainId] === undefined || 32 | addresses[arbChainId] === undefined || 33 | addresses[chainId][tokenName] === undefined || 34 | addresses[arbChainId][l2GName] === undefined 35 | ) { 36 | console.log("Missing dependencies"); 37 | return; 38 | } 39 | 40 | let signers = await ethers.getSigners(); 41 | let addrs = await Promise.all(signers.map(a => a.getAddress())); 42 | 43 | console.log("Signer addrs:", addrs); 44 | 45 | const L1Gateway = await ethers.getContractFactory('L1Gateway'); 46 | let l1Gateway = L1Gateway.attach(addresses[chainId][name]); 47 | 48 | console.log("Deployed addr:", l1Gateway.address); 49 | 50 | await l1Gateway.initialize( 51 | "0x578BAde599406A8fE3d24Fd7f7211c0911F5B29e", 52 | addresses[chainId][tokenName], 53 | addresses[arbChainId][l2GName] 54 | ); 55 | } 56 | 57 | main() 58 | .then(() => process.exit(0)) 59 | .catch((error) => { 60 | console.error(error); 61 | process.exit(1); 62 | }); 63 | 64 | -------------------------------------------------------------------------------- /scripts/arbStake.ts: -------------------------------------------------------------------------------- 1 | import { ethers, upgrades } from 'hardhat'; 2 | import { BigNumber as BN, Signer, Contract } from 'ethers'; 3 | import * as fs from 'fs'; 4 | 5 | 6 | declare module 'ethers' { 7 | interface BigNumber { 8 | e18(this: BigNumber): BigNumber; 9 | } 10 | } 11 | BN.prototype.e18 = function () { 12 | return this.mul(BN.from(10).pow(18)) 13 | } 14 | 15 | 16 | async function main() { 17 | let chainId = (await ethers.provider.getNetwork()).chainId; 18 | console.log("Chain Id:", chainId); 19 | 20 | var addresses: {[key: string]: {[key: string]: string}} = {}; 21 | if(fs.existsSync('address.json')) { 22 | addresses = JSON.parse(fs.readFileSync('address.json', 'utf8')); 23 | } 24 | 25 | if(addresses[chainId] === undefined || 26 | addresses[chainId]['StakeManager'] === undefined || 27 | addresses[chainId]['Pond'] === undefined 28 | ) { 29 | console.log("Missing dependencies"); 30 | return; 31 | } 32 | 33 | let signers = await ethers.getSigners(); 34 | let addrs = await Promise.all(signers.map(a => a.getAddress())); 35 | 36 | console.log("Signer addrs:", addrs); 37 | 38 | const StakeManager = await ethers.getContractFactory('StakeManager'); 39 | let stakeManager = StakeManager.attach(addresses[chainId]['StakeManager']); 40 | 41 | const Pond = await ethers.getContractFactory('Pond'); 42 | let pond = Pond.attach(addresses[chainId]['Pond']); 43 | 44 | let res = await pond.approve(stakeManager.address, 1); 45 | console.log(res); 46 | 47 | res = await res.wait(); 48 | console.log(res); 49 | 50 | res = await stakeManager.createStashAndDelegate( 51 | ['0x5802add45f8ec0a524470683e7295faacc853f97cf4a8d3ffbaaf25ce0fd87c4'], 52 | [1], 53 | '0x41243e6dfa02efb4e5ecefbd93a99d482c3a74c0', 54 | ); 55 | console.log(res); 56 | 57 | res = await res.wait(); 58 | console.log(res); 59 | } 60 | 61 | main() 62 | .then(() => process.exit(0)) 63 | .catch((error) => { 64 | console.error(error); 65 | process.exit(1); 66 | }); 67 | 68 | 69 | -------------------------------------------------------------------------------- /test/helpers/erc165.ts: -------------------------------------------------------------------------------- 1 | import { expect } from "chai"; 2 | import { Contract, Signer } from "ethers"; 3 | import { ethers, network } from "hardhat"; 4 | 5 | export function testERC165( 6 | name: string, 7 | deployer: (signers: Signer[], addrs: string[]) => Promise, 8 | interfaces: { [key: string]: string[] } = {} 9 | ) { 10 | describe(name, function () { 11 | let signers: Signer[]; 12 | let addrs: string[]; 13 | let contract: Contract; 14 | 15 | let snapshot: any; 16 | 17 | before(async function () { 18 | signers = await ethers.getSigners(); 19 | addrs = await Promise.all(signers.map((a) => a.getAddress())); 20 | contract = await deployer(signers, addrs); 21 | }); 22 | 23 | beforeEach(async function () { 24 | snapshot = await network.provider.request({ 25 | method: "evm_snapshot", 26 | params: [], 27 | }); 28 | }); 29 | 30 | afterEach(async function () { 31 | await network.provider.request({ 32 | method: "evm_revert", 33 | params: [snapshot], 34 | }); 35 | }); 36 | 37 | function makeInterfaceId(interfaces: string[]): string { 38 | return ethers.utils.hexlify( 39 | interfaces.map((i) => ethers.utils.arrayify(ethers.utils.id(i).substr(0, 10))).reduce((i1, i2) => i1.map((i, idx) => i ^ i2[idx])) 40 | ); 41 | } 42 | 43 | it("supports ERC165", async function () { 44 | let interfaces = ["supportsInterface(bytes4)"]; 45 | const iid = makeInterfaceId(interfaces); 46 | expect(await contract.supportsInterface(iid)).to.be.true; 47 | }); 48 | 49 | it("does not support 0xffffffff", async function () { 50 | expect(await contract.supportsInterface("0xffffffff")).to.be.false; 51 | }); 52 | 53 | Object.keys(interfaces).map((iname) => { 54 | it(`supports ${iname}`, async function () { 55 | const iid = makeInterfaceId(interfaces[iname]); 56 | expect(await contract.supportsInterface(iid)).to.be.true; 57 | }); 58 | }); 59 | }); 60 | } 61 | -------------------------------------------------------------------------------- /scripts/deploy/staking/RewardDelegatorsInit.ts: -------------------------------------------------------------------------------- 1 | import { ethers, upgrades } from 'hardhat'; 2 | import { BigNumber as BN, Signer, Contract } from 'ethers'; 3 | import * as fs from 'fs'; 4 | 5 | 6 | declare module 'ethers' { 7 | interface BigNumber { 8 | e18(this: BigNumber): BigNumber; 9 | } 10 | } 11 | BN.prototype.e18 = function () { 12 | return this.mul(BN.from(10).pow(18)) 13 | } 14 | 15 | 16 | async function main() { 17 | let chainId = (await ethers.provider.getNetwork()).chainId; 18 | console.log("Chain Id:", chainId); 19 | 20 | var addresses: {[key: string]: {[key: string]: string}} = {}; 21 | if(fs.existsSync('address.json')) { 22 | addresses = JSON.parse(fs.readFileSync('address.json', 'utf8')); 23 | } 24 | 25 | if(addresses[chainId] === undefined || 26 | addresses[chainId]['RewardDelegators'] === undefined || 27 | addresses[chainId]['StakeManager'] === undefined || 28 | addresses[chainId]['ClusterRegistry'] === undefined || 29 | addresses[chainId]['ClusterRewards'] === undefined || 30 | addresses[chainId]['Pond'] === undefined 31 | ) { 32 | console.log("Missing dependencies"); 33 | return; 34 | } 35 | 36 | let signers = await ethers.getSigners(); 37 | let addrs = await Promise.all(signers.map(a => a.getAddress())); 38 | 39 | console.log("Signer addrs:", addrs); 40 | 41 | const RewardDelegators = await ethers.getContractFactory('RewardDelegators'); 42 | let rewardDelegators = await RewardDelegators.attach(addresses[chainId]['RewardDelegators']); 43 | 44 | console.log("Deployed addr:", rewardDelegators.address); 45 | 46 | await rewardDelegators.initialize( 47 | addresses[chainId]['StakeManager'], 48 | addresses[chainId]['ClusterRewards'], 49 | addresses[chainId]['ClusterRegistry'], 50 | addresses[chainId]['Pond'], 51 | ["0x5802add45f8ec0a524470683e7295faacc853f97cf4a8d3ffbaaf25ce0fd87c4", "0x1635815984abab0dbb9afd77984dad69c24bf3d711bc0ddb1e2d53ef2d523e5e"], 52 | [1,1] 53 | ); 54 | } 55 | 56 | main() 57 | .then(() => process.exit(0)) 58 | .catch((error) => { 59 | console.error(error); 60 | process.exit(1); 61 | }); 62 | 63 | 64 | -------------------------------------------------------------------------------- /scripts/deploy/staking/ClusterRewards.ts: -------------------------------------------------------------------------------- 1 | import { ethers, upgrades } from 'hardhat'; 2 | import { BigNumber as BN, Signer, Contract } from 'ethers'; 3 | import * as fs from 'fs'; 4 | 5 | 6 | declare module 'ethers' { 7 | interface BigNumber { 8 | e18(this: BigNumber): BigNumber; 9 | } 10 | } 11 | BN.prototype.e18 = function () { 12 | return this.mul(BN.from(10).pow(18)) 13 | } 14 | 15 | 16 | async function main() { 17 | let chainId = (await ethers.provider.getNetwork()).chainId; 18 | console.log("Chain Id:", chainId); 19 | 20 | var addresses: {[key: string]: {[key: string]: string}} = {}; 21 | if(fs.existsSync('address.json')) { 22 | addresses = JSON.parse(fs.readFileSync('address.json', 'utf8')); 23 | } 24 | 25 | if(addresses[chainId] === undefined || 26 | addresses[chainId]['RewardDelegators'] === undefined 27 | ) { 28 | console.log("Missing dependencies"); 29 | return; 30 | } 31 | 32 | if(addresses[chainId]['ClusterRewards'] !== undefined) { 33 | console.log("Existing deployment:", addresses[chainId]['ClusterRewards']); 34 | return; 35 | } 36 | 37 | let signers = await ethers.getSigners(); 38 | let addrs = await Promise.all(signers.map(a => a.getAddress())); 39 | 40 | console.log("Signer addrs:", addrs); 41 | 42 | const ClusterRewards = await ethers.getContractFactory('ClusterRewards'); 43 | let clusterRewards = await upgrades.deployProxy(ClusterRewards, [ 44 | addrs[0], 45 | addresses[chainId]['RewardDelegators'], 46 | [ethers.utils.id("ETH"), ethers.utils.id("COSMOS"), ethers.utils.id("DOT"), ethers.utils.id("POLYGON")], 47 | [BN.from(6).e18(), BN.from(3).e18(), BN.from(2).e18(), BN.from(1).e18()], 48 | BN.from(1200).e18(), 49 | 100000, 50 | 300 51 | ], { kind: "uups" }); 52 | 53 | console.log("Deployed addr:", clusterRewards.address); 54 | 55 | addresses[chainId]['ClusterRewards'] = clusterRewards.address; 56 | 57 | fs.writeFileSync('address.json', JSON.stringify(addresses, null, 2), 'utf8'); 58 | } 59 | 60 | main() 61 | .then(() => process.exit(0)) 62 | .catch((error) => { 63 | console.error(error); 64 | process.exit(1); 65 | }); 66 | 67 | 68 | -------------------------------------------------------------------------------- /deployments/staking/RewardDelegatorsInit.ts: -------------------------------------------------------------------------------- 1 | import { ethers } from 'hardhat'; 2 | import * as fs from 'fs'; 3 | const config = require('./config'); 4 | 5 | export async function init(rewardDelegators: string, stakeManager: string, clusterRewards: string, clusterRegistry: string) { 6 | let chainId = (await ethers.provider.getNetwork()).chainId; 7 | console.log("Chain Id:", chainId); 8 | 9 | const chainConfig = config[chainId]; 10 | 11 | var addresses: { [key: string]: { [key: string]: string } } = {}; 12 | if (fs.existsSync('address.json')) { 13 | addresses = JSON.parse(fs.readFileSync('address.json', 'utf8')); 14 | } 15 | 16 | if (addresses[chainId] === undefined) { 17 | addresses[chainId] = {}; 18 | } 19 | 20 | let signers = await ethers.getSigners(); 21 | let addrs = await Promise.all(signers.map(a => a.getAddress())); 22 | 23 | console.log("Signer addrs:", addrs); 24 | 25 | const RewardDelegators = await ethers.getContractFactory('RewardDelegators'); 26 | let rewardDelegatorsContract = await RewardDelegators.attach(rewardDelegators); 27 | 28 | const tokenIds = []; 29 | const rewardFactors = []; 30 | const weightForThreshold = []; 31 | const weightForDelegation = []; 32 | 33 | for (let token in chainConfig.staking.tokens) { 34 | const tokenInfo = chainConfig.staking.tokens[token]; 35 | tokenIds.push(tokenInfo.id); 36 | rewardFactors.push(tokenInfo.rewardFactor); 37 | weightForThreshold.push(tokenInfo.weightForThreshold); 38 | weightForDelegation.push(tokenInfo.weightForDelegation); 39 | } 40 | 41 | await rewardDelegatorsContract.initialize( 42 | stakeManager, 43 | clusterRewards, 44 | clusterRegistry, 45 | chainConfig.staking.tokens.POND.address, 46 | tokenIds, 47 | rewardFactors, 48 | weightForThreshold, 49 | weightForDelegation, 50 | [], 51 | [], 52 | ); 53 | 54 | for (let network in chainConfig.staking.thresholdForSelection) { 55 | await rewardDelegatorsContract.updateThresholdForSelection(ethers.utils.id(network), chainConfig.staking.thresholdForSelection[network]); 56 | } 57 | 58 | console.log("Initialized rewardDelegators at", rewardDelegators); 59 | } 60 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@marlinprotocol/contracts", 3 | "version": "2.0.0", 4 | "description": "Contracts pertaining to Marlin", 5 | "directories": { 6 | "test": "test" 7 | }, 8 | "scripts": { 9 | "test": "npx hardhat test", 10 | "benchmark": "npx hardhat test benchmarks/*.ts", 11 | "solhint": "solhint -f table contracts/**/*.sol", 12 | "compile": "npx hardhat compile", 13 | "coverage": "npx hardhat coverage", 14 | "clean": "npx hardhat clean && rm -rf coverage && rm -rf artifacts && npx hardhat typechain", 15 | "prettify:contracts": "npx prettier --write contracts/**/*.sol", 16 | "prettify:test": "npx prettier --write test" 17 | }, 18 | "repository": { 19 | "type": "git", 20 | "url": "https://github.com/marlinprotocol/contracts.git" 21 | }, 22 | "license": "MIT", 23 | "bugs": { 24 | "url": "https://github.com/marlinprotocol/contracts/issues" 25 | }, 26 | "homepage": "https://github.com/marlinprotocol/contracts", 27 | "dependencies": { 28 | "@openzeppelin/contracts": "4.9.2", 29 | "@openzeppelin/contracts-upgradeable": "4.9.2" 30 | }, 31 | "devDependencies": { 32 | "@ethereum-waffle/mock-contract": "^3.4.4", 33 | "@nomicfoundation/hardhat-chai-matchers": "^1.0.6", 34 | "@nomicfoundation/hardhat-network-helpers": "^1.0.8", 35 | "@nomicfoundation/hardhat-verify": "^1.1.1", 36 | "@nomiclabs/hardhat-ethers": "^2.2.3", 37 | "@nomiclabs/hardhat-etherscan": "^3.1.8", 38 | "@openzeppelin/hardhat-upgrades": "^1.28.0", 39 | "@openzeppelin/upgrades-core": "^1.42.1", 40 | "@tenderly/hardhat-tenderly": "^2.5.2", 41 | "@typechain/ethers-v5": "^11.0.0", 42 | "@typechain/hardhat": "^7.0.0", 43 | "@types/chai": "^4.3.5", 44 | "@types/mocha": "^10.0.1", 45 | "@types/node": "^20.3.1", 46 | "chai": "^4.3.7", 47 | "dotenv": "^16.3.0", 48 | "ethereum-waffle": "^4.0.10", 49 | "ethers": "^5.7.2", 50 | "hardhat": "^2.22.0", 51 | "hardhat-gas-reporter": "^1.0.9", 52 | "hardhat-tracer": "^3.1.0", 53 | "solidity-coverage": "^0.8.2", 54 | "ts-node": "^10.9.1", 55 | "typechain": "^8.2.0", 56 | "typescript": "^5.1.3" 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /benchmarks/helpers/deployment.ts: -------------------------------------------------------------------------------- 1 | import { ethers, upgrades } from "hardhat"; 2 | 3 | export function benchmark(name: string, constructorArgs: any[], initArgs: any[]) { 4 | describe(`${name} Deployment`, async () => { 5 | it('deploy logic', async function () { 6 | const ContractFactory = await ethers.getContractFactory(name); 7 | let contract = await ContractFactory.deploy(...constructorArgs); 8 | 9 | let receipt = await contract.deployTransaction.wait(); 10 | 11 | console.log("Gas used: ", receipt.gasUsed.sub(21000).toNumber()); 12 | }); 13 | 14 | it('deploy proxy without initialize', async function () { 15 | const ContractFactory = await ethers.getContractFactory(name); 16 | let contract = await upgrades.deployProxy(ContractFactory, { 17 | kind: "uups", 18 | initializer: false, 19 | constructorArgs 20 | }); 21 | 22 | let receipt = await contract.deployTransaction.wait(); 23 | 24 | console.log("Gas used: ", receipt.gasUsed.sub(21000).toNumber()); 25 | }); 26 | 27 | it('initialize', async function () { 28 | const ContractFactory = await ethers.getContractFactory(name); 29 | let contract = await upgrades.deployProxy(ContractFactory, { 30 | kind: "uups", 31 | initializer: false, 32 | constructorArgs 33 | }); 34 | 35 | let tx = await contract.initialize(...initArgs); 36 | let receipt = await tx.wait(); 37 | 38 | console.log("Gas used: ", receipt.gasUsed.sub(21000).toNumber()); 39 | }); 40 | 41 | it('deploy proxy with initialize', async function () { 42 | const ContractFactory = await ethers.getContractFactory(name); 43 | let contract = await upgrades.deployProxy(ContractFactory, initArgs, 44 | { 45 | kind: "uups", 46 | constructorArgs 47 | }); 48 | 49 | let receipt = await contract.deployTransaction.wait(); 50 | 51 | console.log("Gas used: ", receipt.gasUsed.sub(21000).toNumber()); 52 | }); 53 | }) 54 | } -------------------------------------------------------------------------------- /scripts/deploy/staking/StakeManager.ts: -------------------------------------------------------------------------------- 1 | import { ethers, upgrades } from 'hardhat'; 2 | import { BigNumber as BN, Signer, Contract } from 'ethers'; 3 | import * as fs from 'fs'; 4 | 5 | 6 | declare module 'ethers' { 7 | interface BigNumber { 8 | e18(this: BigNumber): BigNumber; 9 | } 10 | } 11 | BN.prototype.e18 = function () { 12 | return this.mul(BN.from(10).pow(18)) 13 | } 14 | 15 | 16 | async function main() { 17 | let chainId = (await ethers.provider.getNetwork()).chainId; 18 | console.log("Chain Id:", chainId); 19 | 20 | var addresses: {[key: string]: {[key: string]: string}} = {}; 21 | if(fs.existsSync('address.json')) { 22 | addresses = JSON.parse(fs.readFileSync('address.json', 'utf8')); 23 | } 24 | 25 | if(addresses[chainId] === undefined || 26 | addresses[chainId]['Pond'] === undefined || 27 | addresses[chainId]['MPond'] === undefined || 28 | addresses[chainId]['RewardDelegators'] === undefined || 29 | false 30 | ) { 31 | console.log("Missing dependencies"); 32 | return; 33 | } 34 | 35 | if(addresses[chainId]['StakeManager'] !== undefined) { 36 | console.log("Existing deployment:", addresses[chainId]['StakeManager']); 37 | return; 38 | } 39 | 40 | let signers = await ethers.getSigners(); 41 | let addrs = await Promise.all(signers.map(a => a.getAddress())); 42 | 43 | console.log("Signer addrs:", addrs); 44 | 45 | const StakeManager = await ethers.getContractFactory('StakeManager'); 46 | let stakeManager = await upgrades.deployProxy(StakeManager, [ 47 | ["0x5802add45f8ec0a524470683e7295faacc853f97cf4a8d3ffbaaf25ce0fd87c4", "0x1635815984abab0dbb9afd77984dad69c24bf3d711bc0ddb1e2d53ef2d523e5e"], 48 | [addresses[chainId]['Pond'], addresses[chainId]['MPond']], 49 | [false, true], 50 | addresses[chainId]['RewardDelegators'], 51 | 120, 52 | 300, 53 | "0x0000000000000000000000000000000000000000", 54 | ], { kind: "uups" }); 55 | 56 | console.log("Deployed addr:", stakeManager.address); 57 | 58 | addresses[chainId]['StakeManager'] = stakeManager.address; 59 | 60 | fs.writeFileSync('address.json', JSON.stringify(addresses, null, 2), 'utf8'); 61 | } 62 | 63 | main() 64 | .then(() => process.exit(0)) 65 | .catch((error) => { 66 | console.error(error); 67 | process.exit(1); 68 | }); 69 | 70 | 71 | -------------------------------------------------------------------------------- /deployments/bridge/Bridge.ts: -------------------------------------------------------------------------------- 1 | import { ethers, upgrades, run } from 'hardhat'; 2 | import * as fs from 'fs' 3 | 4 | async function main() { 5 | let chainId = (await ethers.provider.getNetwork()).chainId; 6 | console.log("Chain Id:", chainId); 7 | 8 | var addresses: {[key: string]: {[key: string]: string}} = {}; 9 | if(fs.existsSync('address.json')) { 10 | addresses = JSON.parse(fs.readFileSync('address.json', 'utf8')); 11 | } 12 | 13 | if(addresses[chainId] === undefined || 14 | addresses[chainId]['Pond'] === undefined || 15 | addresses[chainId]['MPond'] === undefined || 16 | addresses[chainId]['StakeManager'] === undefined 17 | ) { 18 | console.log("Missing dependencies"); 19 | return; 20 | } 21 | 22 | if(addresses[chainId]['Bridge'] !== undefined) { 23 | console.log("Existing deployment:", addresses[chainId]['Bridge']); 24 | return; 25 | } 26 | 27 | let signers = await ethers.getSigners(); 28 | let addrs = await Promise.all(signers.map(a => a.getAddress())); 29 | 30 | console.log("Signer addrs:", addrs); 31 | 32 | const Bridge = await ethers.getContractFactory('Bridge'); 33 | let bridge = await upgrades.deployProxy(Bridge, [ 34 | addresses[chainId]['MPond'], 35 | addresses[chainId]['Pond'], 36 | addresses[chainId]['StakeManager'] 37 | ], { kind: "uups" }); 38 | 39 | console.log("Deployed addr:", bridge.address); 40 | 41 | addresses[chainId]['Bridge'] = bridge.address; 42 | 43 | fs.writeFileSync('address.json', JSON.stringify(addresses, null, 2), 'utf8'); 44 | } 45 | 46 | async function verify() { 47 | let chainId = (await ethers.provider.getNetwork()).chainId; 48 | console.log("Chain Id:", chainId); 49 | 50 | var addresses: {[key: string]: {[key: string]: string}} = {}; 51 | if(fs.existsSync('address.json')) { 52 | addresses = JSON.parse(fs.readFileSync('address.json', 'utf8')); 53 | } 54 | 55 | if( 56 | addresses[chainId] === undefined || 57 | addresses[chainId]['Bridge'] === undefined 58 | ) { 59 | console.log("Missing dependencies"); 60 | return; 61 | } 62 | 63 | await run("verify:verify", { 64 | address: addresses[chainId]['Bridge'], 65 | constructorArguments: [] 66 | }); 67 | } 68 | 69 | main() 70 | .then(() => process.exit(0)) 71 | .catch((error) => { 72 | console.error(error); 73 | process.exit(1); 74 | }); 75 | 76 | -------------------------------------------------------------------------------- /scripts/helper/helper.ts: -------------------------------------------------------------------------------- 1 | import { ethers } from "hardhat"; 2 | import fs from "fs"; 3 | import { 4 | run, 5 | } from 'hardhat'; 6 | 7 | 8 | export async function getConfig() { 9 | const chainId = (await ethers.provider.getNetwork()).chainId.toString(); 10 | console.log("Running script on chain id:", chainId); 11 | 12 | const signers = await ethers.getSigners(); 13 | 14 | console.log("Signers:", signers); 15 | for (let i = 0; i < 5; i++) { 16 | if (!signers[i]) { 17 | break; 18 | } 19 | console.log(signers[i].address); 20 | } 21 | if (signers.length > 5) { 22 | console.log(`... and ${signers.length - 5} more`); 23 | } 24 | 25 | const path = `./addresses/${chainId}.json`; 26 | console.log("Path:", path); 27 | let addresses = JSON.parse(fs.readFileSync(path, "utf-8")); 28 | 29 | if (!Object.keys(addresses).length) { 30 | const addressesInit = { 31 | proxy: {}, 32 | implementation: {}, 33 | }; 34 | fs.writeFileSync(path, JSON.stringify(addressesInit, null, 4), "utf-8"); 35 | addresses = addressesInit; 36 | } 37 | 38 | return { chainId, signers, path, addresses }; 39 | } 40 | 41 | export enum ContractType { 42 | Proxy = "proxy", 43 | Implementation = "implementation", 44 | } 45 | 46 | export const verifyContract = async (contractName: string, contractType: ContractType, constructorArguments: any[] = []) => { 47 | const { addresses } = await getConfig(); 48 | const isProxy = contractType === ContractType.Proxy; 49 | const type = isProxy ? "proxy" : "implementation"; 50 | 51 | // Verify in Explorer 52 | try { 53 | console.log(`Verifying ${contractName} ${type}...`); 54 | console.log("constructor args: ", constructorArguments); 55 | 56 | // if first character is Upper case, then convert to lower case 57 | let contractNameLowerCase = contractName; 58 | if (contractName.charAt(0) === contractName.charAt(0).toUpperCase()) { 59 | contractNameLowerCase = contractName.charAt(0).toLowerCase() + contractName.slice(1); 60 | } 61 | 62 | // verify 63 | await run("verify:verify", { 64 | address: addresses[type][contractNameLowerCase], 65 | constructorArguments: constructorArguments, 66 | }); 67 | console.log(`(${type}) ${contractName} verified\n`); 68 | } catch (error) { 69 | console.log(`[${contractName}]: `, error); 70 | } 71 | }; -------------------------------------------------------------------------------- /scripts/populateTree.ts: -------------------------------------------------------------------------------- 1 | import { ethers } from "hardhat"; 2 | import * as fs from "fs"; 3 | import fetch from "node-fetch"; 4 | 5 | import { RewardDelegators__factory, RewardDelegators } from "../typechain-types"; 6 | 7 | const subraphUrl = "https://api.thegraph.com/subgraphs/name/marlinprotocol/staking-kovan"; 8 | const networkId = "0x0000"; 9 | 10 | async function main() { 11 | let signers = await ethers.getSigners(); 12 | let admin = signers[0]; 13 | let chainId = (await ethers.provider.getNetwork()).chainId; 14 | console.log("Chain Id:", chainId); 15 | 16 | var addresses: { [key: string]: { [key: string]: string } } = {}; 17 | if (fs.existsSync("address.json")) { 18 | addresses = JSON.parse(fs.readFileSync("address.json", "utf8")); 19 | } 20 | 21 | const relevantContractAddresses = addresses[chainId]; 22 | const clusters = (await getClusters(subraphUrl)).map((a) => a.id) as string[]; 23 | 24 | const rewardDelegators = RewardDelegators__factory.connect(relevantContractAddresses.RewardDelegators, admin); 25 | const tx = await rewardDelegators.refreshClusterDelegation(networkId, clusters); 26 | return { relevantContractAddresses, clusters, receipt: await tx.wait() }; 27 | } 28 | 29 | main().then(console.log); 30 | 31 | async function getClusters(url: string): Promise { 32 | let skip = 0; 33 | const countPerQuery = 999; 34 | const allData = []; 35 | for (;;) { 36 | const data = JSON.stringify({ 37 | query: `{ 38 | clusters(first: ${countPerQuery}, skip: ${countPerQuery * skip}) { 39 | id 40 | } 41 | }`, 42 | }); 43 | 44 | const options = { 45 | url, 46 | method: "post", 47 | headers: { "Content-Type": "application/json" }, 48 | body: data, 49 | }; 50 | 51 | const result = await (await fetchData(options)).json(); 52 | if (result.errors) { 53 | throw new Error("Error while fetching data from subgraph"); 54 | } else if (result.data.clusters.length === 0) { 55 | return allData; 56 | } else { 57 | skip++; 58 | allData.push(...result.data.clusters); 59 | } 60 | } 61 | } 62 | 63 | export async function fetchData(requestData: any): Promise { 64 | const data = await fetch(requestData.url, { headers: requestData.headers, method: requestData.method, body: requestData.body }); 65 | return data; 66 | } 67 | -------------------------------------------------------------------------------- /deployments/staking/RewardDelegators.ts: -------------------------------------------------------------------------------- 1 | import { ethers, run, upgrades } from 'hardhat'; 2 | import { Contract } from 'ethers'; 3 | import * as fs from 'fs'; 4 | import { upgrade as upgradeUtil } from '../utils/Upgrade'; 5 | 6 | export async function deployNoInit(): Promise { 7 | let chainId = (await ethers.provider.getNetwork()).chainId; 8 | console.log("Chain Id:", chainId); 9 | 10 | var addresses: {[key: string]: {[key: string]: string}} = {}; 11 | if(fs.existsSync('address.json')) { 12 | addresses = JSON.parse(fs.readFileSync('address.json', 'utf8')); 13 | } 14 | 15 | if(addresses[chainId] === undefined) { 16 | addresses[chainId] = {}; 17 | } 18 | 19 | const RewardDelegators = await ethers.getContractFactory('RewardDelegators'); 20 | if(addresses[chainId]['RewardDelegators'] !== undefined) { 21 | console.log("Existing deployment:", addresses[chainId]['RewardDelegators']); 22 | return RewardDelegators.attach(addresses[chainId]['RewardDelegators']); 23 | } 24 | 25 | let signers = await ethers.getSigners(); 26 | let addrs = await Promise.all(signers.map(a => a.getAddress())); 27 | 28 | console.log("Signer addrs:", addrs); 29 | 30 | let rewardDelegators = await upgrades.deployProxy(RewardDelegators, { kind: "uups", initializer: false }); 31 | 32 | console.log("Deployed addr:", rewardDelegators.address); 33 | 34 | addresses[chainId]['RewardDelegators'] = rewardDelegators.address; 35 | 36 | fs.writeFileSync('address.json', JSON.stringify(addresses, null, 2), 'utf8'); 37 | 38 | return rewardDelegators; 39 | } 40 | 41 | export async function upgrade() { 42 | await upgradeUtil('RewardDelegators', 'RewardDelegators', []); 43 | } 44 | 45 | export async function verify() { 46 | let chainId = (await ethers.provider.getNetwork()).chainId; 47 | console.log("Chain Id:", chainId); 48 | 49 | var addresses: {[key: string]: {[key: string]: string}} = {}; 50 | if(fs.existsSync('address.json')) { 51 | addresses = JSON.parse(fs.readFileSync('address.json', 'utf8')); 52 | } 53 | 54 | if(addresses[chainId] === undefined || addresses[chainId]['RewardDelegators'] === undefined) { 55 | throw new Error("Reward Delegators not deployed"); 56 | } 57 | 58 | const implAddress = await upgrades.erc1967.getImplementationAddress(addresses[chainId]['RewardDelegators']); 59 | 60 | await run("verify:verify", { 61 | address: implAddress, 62 | constructorArguments: [] 63 | }); 64 | 65 | console.log("Reward Delegators verified"); 66 | } -------------------------------------------------------------------------------- /deployments/staking/ClusterRegistry.ts: -------------------------------------------------------------------------------- 1 | import { ethers, upgrades, run } from 'hardhat'; 2 | import { Contract } from 'ethers'; 3 | import * as fs from 'fs'; 4 | import { upgrade as upgradeUtil } from '../utils/Upgrade'; 5 | const config = require('./config'); 6 | 7 | export async function deploy(rewardDelegatorsAddress: string, noLog?: boolean): Promise { 8 | let chainId = (await ethers.provider.getNetwork()).chainId; 9 | 10 | const chainConfig = config[chainId]; 11 | 12 | const ClusterRegistry = await ethers.getContractFactory('ClusterRegistry'); 13 | 14 | var addresses: {[key: string]: {[key: string]: string}} = {}; 15 | if(!noLog) { 16 | console.log("Chain Id:", chainId); 17 | 18 | if(fs.existsSync('address.json')) { 19 | addresses = JSON.parse(fs.readFileSync('address.json', 'utf8')); 20 | } 21 | 22 | if(addresses[chainId] === undefined) { 23 | addresses[chainId] = {}; 24 | } 25 | 26 | if(addresses[chainId]['ClusterRegistry'] !== undefined) { 27 | console.log("Existing deployment:", addresses[chainId]['ClusterRegistry']); 28 | return ClusterRegistry.attach(addresses[chainId]['ClusterRegistry']); 29 | } 30 | } 31 | 32 | const waitTimes = chainConfig.cluster.waitTimes; 33 | 34 | let clusterRegistry = await upgrades.deployProxy(ClusterRegistry, [waitTimes, rewardDelegatorsAddress], { kind: "uups" }); 35 | 36 | if(!noLog) { 37 | console.log("Deployed addr:", clusterRegistry.address); 38 | 39 | addresses[chainId]['ClusterRegistry'] = clusterRegistry.address; 40 | 41 | fs.writeFileSync('address.json', JSON.stringify(addresses, null, 2), 'utf8'); 42 | } 43 | 44 | return clusterRegistry; 45 | } 46 | 47 | export async function upgrade() { 48 | await upgradeUtil('ClusterRegistry', 'ClusterRegistry', []); 49 | } 50 | 51 | export async function verify() { 52 | let chainId = (await ethers.provider.getNetwork()).chainId; 53 | console.log("Chain Id:", chainId); 54 | 55 | var addresses: {[key: string]: {[key: string]: string}} = {}; 56 | if(fs.existsSync('address.json')) { 57 | addresses = JSON.parse(fs.readFileSync('address.json', 'utf8')); 58 | } 59 | 60 | if(addresses[chainId] === undefined || addresses[chainId]['ClusterRegistry'] === undefined) { 61 | throw new Error("Cluster Registry not deployed"); 62 | } 63 | 64 | const implAddress = await upgrades.erc1967.getImplementationAddress(addresses[chainId]['ClusterRegistry']); 65 | 66 | await run("verify:verify", { 67 | address: implAddress, 68 | constructorArguments: [] 69 | }); 70 | 71 | console.log("Cluster Registry verified"); 72 | } -------------------------------------------------------------------------------- /test/governance/Timelock.ts: -------------------------------------------------------------------------------- 1 | import { ethers, upgrades } from "hardhat"; 2 | import { expect, util } from "chai"; 3 | import { BigNumber as BN, Signer } from "ethers"; 4 | import { sign } from "crypto"; 5 | import { getTimelock } from "../../utils/typechainConvertor"; 6 | 7 | declare module "ethers" { 8 | interface BigNumber { 9 | e18(this: BigNumber): BigNumber; 10 | } 11 | } 12 | 13 | BN.prototype.e18 = function () { 14 | return this.mul(BN.from(10).pow(18)); 15 | }; 16 | 17 | describe("Timelock", function () { 18 | let signers: Signer[]; 19 | let addrs: string[]; 20 | beforeEach(async function () { 21 | signers = await ethers.getSigners(); 22 | addrs = await Promise.all(signers.map((a) => a.getAddress())); 23 | }); 24 | 25 | it("deploys with initialization disabled", async function () { 26 | const Timelock = await ethers.getContractFactory("Timelock"); 27 | let timelockContract = await Timelock.deploy(); 28 | let timelock = getTimelock(timelockContract.address, signers[0]); 29 | await expect(timelock.initialize(2 * 24 * 60 * 60)).to.be.reverted; 30 | }); 31 | 32 | it("deploys as proxy and initializes", async function () { 33 | const Timelock = await ethers.getContractFactory("Timelock"); 34 | let timelockContract = await upgrades.deployProxy(Timelock, [2 * 24 * 60 * 60], { kind: "uups" }); 35 | let timelock = getTimelock(timelockContract.address, signers[0]); 36 | 37 | expect(await timelock.delay()).to.equal(2 * 24 * 60 * 60); 38 | expect(await timelock.hasRole(await timelock.DEFAULT_ADMIN_ROLE(), addrs[0])).to.be.true; 39 | }); 40 | 41 | it("upgrades", async function () { 42 | const Timelock = await ethers.getContractFactory("Timelock"); 43 | let timelockContract = await upgrades.deployProxy(Timelock, [2 * 24 * 60 * 60], { kind: "uups" }); 44 | let timelock = getTimelock(timelockContract.address, signers[0]); 45 | 46 | await upgrades.upgradeProxy(timelock.address, Timelock, { kind: "uups" }); 47 | 48 | expect(await timelock.delay()).to.equal(2 * 24 * 60 * 60); 49 | expect(await timelock.hasRole(await timelock.DEFAULT_ADMIN_ROLE(), addrs[0])).to.be.true; 50 | }); 51 | 52 | it("does not upgrade without admin", async function () { 53 | const Timelock = await ethers.getContractFactory("Timelock"); 54 | let timelockContract = await upgrades.deployProxy(Timelock, [2 * 24 * 60 * 60], { kind: "uups" }); 55 | let timelock = getTimelock(timelockContract.address, signers[0]); 56 | 57 | await expect(upgrades.upgradeProxy(timelock.address, Timelock.connect(signers[1]), { kind: "uups" })).to.be.reverted; 58 | }); 59 | }); 60 | -------------------------------------------------------------------------------- /deployments/staking/StakeManager.ts: -------------------------------------------------------------------------------- 1 | import { ethers, run, upgrades } from 'hardhat'; 2 | import { Contract } from 'ethers'; 3 | import * as fs from 'fs'; 4 | import { upgrade as upgradeUtil } from '../utils/Upgrade'; 5 | const config = require('./config'); 6 | 7 | export async function deploy(rewardDelegators: string): Promise { 8 | let chainId = (await ethers.provider.getNetwork()).chainId; 9 | console.log("Chain Id:", chainId); 10 | 11 | const chainConfig = config[chainId]; 12 | 13 | var addresses: {[key: string]: {[key: string]: string}} = {}; 14 | if(fs.existsSync('address.json')) { 15 | addresses = JSON.parse(fs.readFileSync('address.json', 'utf8')); 16 | } 17 | 18 | if(addresses[chainId] === undefined) { 19 | addresses[chainId] = {}; 20 | } 21 | 22 | const StakeManager = await ethers.getContractFactory('StakeManager'); 23 | if(addresses[chainId]['StakeManager'] !== undefined) { 24 | console.log("Existing deployment:", addresses[chainId]['StakeManager']); 25 | return StakeManager.attach(addresses[chainId]['StakeManager']); 26 | } 27 | 28 | let signers = await ethers.getSigners(); 29 | let addrs = await Promise.all(signers.map(a => a.getAddress())); 30 | 31 | console.log("Signer addrs:", addrs); 32 | 33 | const tokenIds: string[] = []; 34 | const tokenAddresses: string[] = []; 35 | const delegatable: boolean[] = []; 36 | 37 | for(let token in chainConfig.staking.tokens) { 38 | const tokenInfo = chainConfig.staking.tokens[token]; 39 | tokenIds.push(tokenInfo.id); 40 | tokenAddresses.push(tokenInfo.address); 41 | delegatable.push(tokenInfo.delegatable); 42 | } 43 | 44 | let stakeManager = await upgrades.deployProxy(StakeManager, [ 45 | tokenIds, 46 | tokenAddresses, 47 | delegatable, 48 | rewardDelegators, 49 | chainConfig.staking.waitTimes.redelegation, 50 | chainConfig.staking.waitTimes.undelegation 51 | ], { kind: "uups" }); 52 | 53 | console.log("Deployed addr:", stakeManager.address); 54 | 55 | addresses[chainId]['StakeManager'] = stakeManager.address; 56 | 57 | fs.writeFileSync('address.json', JSON.stringify(addresses, null, 2), 'utf8'); 58 | 59 | return stakeManager; 60 | } 61 | 62 | export async function upgrade() { 63 | await upgradeUtil('StakeManager', 'StakeManager', []); 64 | } 65 | 66 | export async function verify() { 67 | let chainId = (await ethers.provider.getNetwork()).chainId; 68 | console.log("Chain Id:", chainId); 69 | 70 | var addresses: {[key: string]: {[key: string]: string}} = {}; 71 | if(fs.existsSync('address.json')) { 72 | addresses = JSON.parse(fs.readFileSync('address.json', 'utf8')); 73 | } 74 | 75 | if(addresses[chainId] === undefined || addresses[chainId]['StakeManager'] === undefined) { 76 | throw new Error("Stake Manager not deployed"); 77 | } 78 | 79 | const implAddress = await upgrades.erc1967.getImplementationAddress(addresses[chainId]['StakeManager']); 80 | 81 | await run("verify:verify", { 82 | address: implAddress, 83 | constructorArguments: [] 84 | }); 85 | 86 | console.log("Stake Manager verified"); 87 | } -------------------------------------------------------------------------------- /deployments/tokens/pond.js: -------------------------------------------------------------------------------- 1 | const networks = require("../networks"); 2 | const deployContract = require("../helpers"); 3 | 4 | if (!networks[process.env.NETWORK]) { 5 | console.log("NETWORK not declared or valid in env variables"); 6 | process.exit(1); 7 | } else { 8 | console.log(`Deploying contracts on ${networks[process.env.NETWORK]}`); 9 | } 10 | 11 | const Web3 = require("web3"); 12 | const web3 = new Web3(networks[process.env.NETWORK]); 13 | 14 | var proxyAdmin; 15 | 16 | if (!process.env.PRIV_KEY) { 17 | console.log("PRIV_KEY not defined in env variables"); 18 | } else { 19 | web3.eth.accounts.wallet.add(process.env.PRIV_KEY); 20 | } 21 | 22 | if (!process.env.PROXY_ADMIN) { 23 | console.log("PROXY_ADMIN is not defined"); 24 | process.exit(1); 25 | } else { 26 | proxyAdmin = process.env.PROXY_ADMIN; 27 | } 28 | 29 | const config = { 30 | deploymentConfig: { 31 | gas: 2000000, 32 | gasPrice: 54000000000, 33 | from: web3.eth.accounts.wallet[0].address, 34 | }, 35 | }; 36 | 37 | // standard script 38 | const TokenLogicCompiled = require("../build/contracts/TokenLogic.json"); 39 | const TokenProxyCompiled = require("../build/contracts/TokenProxy.json"); 40 | 41 | async function deploy() { 42 | const TokenLogicAddress = await deployContract( 43 | web3, 44 | TokenLogicCompiled.abi, 45 | TokenLogicCompiled.bytecode, 46 | [], 47 | config.deploymentConfig 48 | ); 49 | const TokenProxyAddress = await deployContract( 50 | web3, 51 | TokenProxyCompiled.abi, 52 | TokenProxyCompiled.bytecode, 53 | [TokenLogicAddress, proxyAdmin], 54 | config.deploymentConfig 55 | ); 56 | return { 57 | TokenLogicAddress, 58 | TokenProxyAddress, 59 | }; 60 | } 61 | 62 | deploy().then(console.table).catch(console.log); 63 | 64 | // Deploying contracts on https://kovan.infura.io/v3/9dc997986f8840daa0e6ccb1d8d0d757 65 | // 0x55aafb0d5ecc420087ccd56c340b458f2e5da104013cd4a352d2082d86469aaf 66 | // 0x880820dee8446a232cc18ba8bd2df5536df1951225cca820ef0c14c1b4ba8c2e 67 | // ┌───────────────────┬──────────────────────────────────────────────┐ 68 | // │ (index) │ Values │ 69 | // ├───────────────────┼──────────────────────────────────────────────┤ 70 | // │ TokenLogicAddress │ '0xe01571d3063d39998bd97F7d74c6b90215C7caaf' │ 71 | // │ TokenProxyAddress │ '0x92A583dB9F7cBA4847b290A0B5e9E8e28030643c' │ 72 | // └───────────────────┴──────────────────────────────────────────────┘ 73 | 74 | // Deploying contracts on https://mainnet.infura.io/v3/9dc997986f8840daa0e6ccb1d8d0d757 75 | // 0xec452a57179ca048d3b27436daaf5f2f5250eb014d63b217eb4fbea655691f07 76 | // 0xbb74d42076661cfd175537ac46f5ce084b97a384588007f737d00aaf19908173 77 | // ┌───────────────────┬──────────────────────────────────────────────┐ 78 | // │ (index) │ Values │ 79 | // ├───────────────────┼──────────────────────────────────────────────┤ 80 | // │ TokenLogicAddress │ '0x9777BE0b086C3cc24EF4Ec8CE3bba3556B3AC31b' │ 81 | // │ TokenProxyAddress │ '0xDe045Ef9159d91CF93Cc89F4F2173632b1A8d6b8' │ 82 | // └───────────────────┴──────────────────────────────────────────────┘ 83 | -------------------------------------------------------------------------------- /deployments/staking/ClusterRewards.ts: -------------------------------------------------------------------------------- 1 | import { ethers, run, upgrades } from 'hardhat'; 2 | import { Contract } from 'ethers'; 3 | import * as fs from 'fs'; 4 | import { upgrade as upgradeUtil } from '../utils/Upgrade'; 5 | const config = require('./config'); 6 | 7 | export async function deploy(rewardDelegators: string, receiverStaking: string, clusterSelectorMap: any, admin?: string, noLog?: boolean): Promise { 8 | let chainId = (await ethers.provider.getNetwork()).chainId; 9 | 10 | const chainConfig = config[chainId]; 11 | 12 | const ClusterRewards = await ethers.getContractFactory('ClusterRewards'); 13 | 14 | var addresses: {[key: string]: {[key: string]: string}} = {}; 15 | if(!noLog) { 16 | console.log("Chain Id:", chainId); 17 | 18 | if(fs.existsSync('address.json')) { 19 | addresses = JSON.parse(fs.readFileSync('address.json', 'utf8')); 20 | } 21 | 22 | if(addresses[chainId] === undefined) { 23 | addresses[chainId] = {}; 24 | } 25 | 26 | if(addresses[chainId]['ClusterRewards'] !== undefined) { 27 | console.log("Existing deployment:", addresses[chainId]['ClusterRewards']); 28 | return ClusterRewards.attach(addresses[chainId]['ClusterRewards']); 29 | } 30 | } 31 | 32 | 33 | const networkIds: string[] = []; 34 | const rewardWeights: string[] = []; 35 | const clusterSelectors: string[] = []; 36 | 37 | for(let network in chainConfig.staking.rewardWeights) { 38 | networkIds.push(ethers.utils.id(network)); 39 | rewardWeights.push(chainConfig.staking.rewardWeights[network]); 40 | clusterSelectors.push(clusterSelectorMap[network]); 41 | } 42 | 43 | if(!admin) admin = chainConfig.admin; 44 | 45 | let clusterRewards = await upgrades.deployProxy(ClusterRewards, [ 46 | admin, 47 | rewardDelegators, 48 | receiverStaking, 49 | networkIds, 50 | rewardWeights, 51 | clusterSelectors, 52 | chainConfig.totalRewardsPerEpoch 53 | ], { kind: "uups" }); 54 | 55 | if(!noLog) { 56 | console.log("Deployed addr:", clusterRewards.address); 57 | 58 | addresses[chainId]['ClusterRewards'] = clusterRewards.address; 59 | 60 | fs.writeFileSync('address.json', JSON.stringify(addresses, null, 2), 'utf8'); 61 | } 62 | 63 | return clusterRewards; 64 | } 65 | 66 | export async function upgrade() { 67 | await upgradeUtil('ClusterRewards', 'ClusterRewards', []); 68 | } 69 | 70 | export async function verify() { 71 | let chainId = (await ethers.provider.getNetwork()).chainId; 72 | console.log("Chain Id:", chainId); 73 | 74 | var addresses: {[key: string]: {[key: string]: string}} = {}; 75 | if(fs.existsSync('address.json')) { 76 | addresses = JSON.parse(fs.readFileSync('address.json', 'utf8')); 77 | } 78 | 79 | if(addresses[chainId] === undefined || addresses[chainId]['ClusterRewards'] === undefined) { 80 | throw new Error("Cluster Rewards not deployed"); 81 | } 82 | 83 | const implAddress = await upgrades.erc1967.getImplementationAddress(addresses[chainId]['ClusterRewards']); 84 | 85 | await run("verify:verify", { 86 | address: implAddress, 87 | constructorArguments: [] 88 | }); 89 | 90 | console.log("Cluster Rewards verified"); 91 | } 92 | -------------------------------------------------------------------------------- /benchmarks/fixtures/ClusterSelector.ts: -------------------------------------------------------------------------------- 1 | import { ethers, upgrades } from "hardhat"; 2 | import { BigNumber, BigNumberish, Signer, utils, Wallet } from "ethers"; 3 | import { deployMockContract } from "@ethereum-waffle/mock-contract"; 4 | import { deploy as deployClusterSelector } from "../../deployments/staking/ClusterSelector"; 5 | import { ClusterSelector__factory } from "../../typechain-types"; 6 | 7 | const EPOCH_LENGTH = 15*60; 8 | 9 | export async function deployFixture() { 10 | const signers = await ethers.getSigners(); 11 | const addrs = await Promise.all(signers.map((a) => a.getAddress())); 12 | 13 | const blockNum = await ethers.provider.getBlockNumber(); 14 | const blockData = await ethers.provider.getBlock(blockNum); 15 | 16 | // TODO: mock arbGasInfo precompile might skew gas estimate for precompile call slightly 17 | const arbGasInfoMock = await deployMockContract(signers[0], ["function getPricesInArbGas() view returns (uint, uint, uint)"]); 18 | await arbGasInfoMock.mock.getPricesInArbGas.returns(223148, 1593, 21000); 19 | const clusterSelectorInstance = await deployClusterSelector( 20 | "ETH", // network 21 | addrs[1], // rewardDelegators 22 | arbGasInfoMock.address, // arbGasInfo 23 | addrs[2], // admin 24 | blockData.timestamp, // startTime 25 | EPOCH_LENGTH, // epochLength 26 | "0", // gas refund, 27 | ethers.utils.parseEther("0.0001").toString(), // max reward 28 | true 29 | ); 30 | 31 | 32 | return { 33 | arbGasInfoMock, 34 | clusterSelector: ClusterSelector__factory.connect(clusterSelectorInstance.address, signers[0]), 35 | admin: signers[2], 36 | rewardDelegatorsMock: signers[1] 37 | }; 38 | } 39 | 40 | export async function initDataFixture() { 41 | if(!process.env.NO_OF_CLUSTERS) throw new Error("NO_OF_CLUSTERS not set"); 42 | const noOfClusters: number = parseInt(process.env.NO_OF_CLUSTERS); 43 | 44 | const signers = await ethers.getSigners(); 45 | const preAllocEthSigner = signers[8]; 46 | 47 | const { 48 | arbGasInfoMock, 49 | clusterSelector, 50 | admin, 51 | rewardDelegatorsMock 52 | } = await deployFixture(); 53 | 54 | await preAllocEthSigner.sendTransaction({to: clusterSelector.address, value: ethers.utils.parseEther("100")}); 55 | 56 | const tokenSupply: BigNumber = BigNumber.from(10).pow(28); 57 | 58 | const clusters: string[] = []; 59 | const balances: BigNumberish[] = []; 60 | 61 | // generate clusters and balance data 62 | for(let i=0; i < noOfClusters; i++) { 63 | const address = Wallet.createRandom().address; 64 | clusters.push(address); 65 | balances.push(BigNumber.from(ethers.utils.randomBytes(32)).mod(tokenSupply.div(utils.parseEther(noOfClusters+"")))); 66 | } 67 | 68 | // insert clusterData into selector 69 | for(let i=0; i < clusters.length; i+=100) { 70 | await clusterSelector.connect(rewardDelegatorsMock).upsertMultiple(clusters.slice(i, i+100), balances.slice(i, i+100)); 71 | } 72 | 73 | return { 74 | arbGasInfoMock, 75 | clusterSelector, 76 | admin, 77 | rewardDelegatorsMock, 78 | nodesInserted: noOfClusters 79 | }; 80 | } -------------------------------------------------------------------------------- /benchmarks/Pond.ts: -------------------------------------------------------------------------------- 1 | import { ethers, upgrades } from 'hardhat'; 2 | import { BigNumber as BN, Signer, Contract } from 'ethers'; 3 | import { benchmark as benchmarkDeployment } from './helpers/deployment'; 4 | 5 | 6 | declare module 'ethers' { 7 | interface BigNumber { 8 | e18(this: BigNumber): BigNumber; 9 | } 10 | } 11 | BN.prototype.e18 = function () { 12 | return this.mul(BN.from(10).pow(18)) 13 | } 14 | 15 | 16 | describe('Pond', function () { 17 | let signers: Signer[]; 18 | let addrs: string[]; 19 | 20 | beforeEach(async function () { 21 | signers = await ethers.getSigners(); 22 | addrs = await Promise.all(signers.map(a => a.getAddress())); 23 | }); 24 | 25 | benchmarkDeployment('Pond', [], ["Marlin POND", "POND"]); 26 | 27 | it('transfer all to new address', async function () { 28 | const Pond = await ethers.getContractFactory('Pond'); 29 | let pond = await upgrades.deployProxy(Pond, ["Marlin POND", "POND"], { kind: "uups" }); 30 | 31 | let tx = await pond.transfer(addrs[1], BN.from(10e9).e18()); 32 | let receipt = await tx.wait(); 33 | 34 | console.log("Gas used: ", receipt.gasUsed.sub(21000).toNumber()); 35 | }); 36 | 37 | it('transfer all to old address', async function () { 38 | const Pond = await ethers.getContractFactory('Pond'); 39 | let pond = await upgrades.deployProxy(Pond, ["Marlin POND", "POND"], { kind: "uups" }); 40 | await pond.transfer(addrs[1], BN.from(1e9).e18()); 41 | 42 | let tx = await pond.transfer(addrs[1], BN.from(9e9).e18()); 43 | let receipt = await tx.wait(); 44 | 45 | console.log("Gas used: ", receipt.gasUsed.sub(21000).toNumber()); 46 | }); 47 | 48 | it('transfer some to old address', async function () { 49 | const Pond = await ethers.getContractFactory('Pond'); 50 | let pond = await upgrades.deployProxy(Pond, ["Marlin POND", "POND"], { kind: "uups" }); 51 | await pond.transfer(addrs[1], BN.from(1e9).e18()); 52 | 53 | let tx = await pond.transfer(addrs[1], BN.from(8e9).e18()); 54 | let receipt = await tx.wait(); 55 | 56 | console.log("Gas used: ", receipt.gasUsed.sub(21000).toNumber()); 57 | }); 58 | 59 | it('transfer some to new address', async function () { 60 | const Pond = await ethers.getContractFactory('Pond'); 61 | let pond = await upgrades.deployProxy(Pond, ["Marlin POND", "POND"], { kind: "uups" }); 62 | 63 | let tx = await pond.transfer(addrs[1], BN.from(8e9).e18()); 64 | let receipt = await tx.wait(); 65 | 66 | console.log("Gas used: ", receipt.gasUsed.sub(21000).toNumber()); 67 | }); 68 | 69 | it('approve new', async function () { 70 | const Pond = await ethers.getContractFactory('Pond'); 71 | let pond = await upgrades.deployProxy(Pond, ["Marlin POND", "POND"], { kind: "uups" }); 72 | 73 | let tx = await pond.approve(addrs[1], BN.from(8e9).e18()); 74 | let receipt = await tx.wait(); 75 | 76 | console.log("Gas used: ", receipt.gasUsed.sub(21000).toNumber()); 77 | }); 78 | 79 | it('approve old', async function () { 80 | const Pond = await ethers.getContractFactory('Pond'); 81 | let pond = await upgrades.deployProxy(Pond, ["Marlin POND", "POND"], { kind: "uups" }); 82 | await pond.approve(addrs[1], BN.from(1e9).e18()); 83 | 84 | let tx = await pond.approve(addrs[1], BN.from(8e9).e18()); 85 | let receipt = await tx.wait(); 86 | 87 | console.log("Gas used: ", receipt.gasUsed.sub(21000).toNumber()); 88 | }); 89 | }); 90 | 91 | -------------------------------------------------------------------------------- /contracts/staking/interfaces/IClusterSelector.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | interface IClusterSelector { 6 | /// @notice Add an element to tree. If the element already exists, it will be updated 7 | /// @param newNode Address of the node to add 8 | /// @param balance Balance of the node 9 | function upsert(address newNode, uint64 balance) external; 10 | 11 | /// @notice function add multiple addresses in one call 12 | /// @param newNodes newNodes of the node nodes 13 | /// @param balances Balances of the new nodes. 14 | function upsertMultiple(address[] calldata newNodes, uint64[] calldata balances) external; 15 | 16 | /// @notice Update the balance of the node 17 | /// @param cluster Address of the existing node 18 | /// @param clusterBalance new balance of the node 19 | function update_unchecked(address cluster, uint64 clusterBalance) external; 20 | 21 | /// @notice Delete a node from the tree 22 | /// @param key Address of the node to delete 23 | function delete_unchecked(address key) external; 24 | 25 | /// @notice Insert the node with given balance (unchecked) 26 | /// @param newNode Address of the new node 27 | /// @param balance Balance of the new node 28 | function insert_unchecked(address newNode, uint64 balance) external; 29 | 30 | /// @notice Insert multiple nodes with given balances (unchecked) 31 | /// @param newNodes Addresses of the new nodes 32 | /// @param balances Balances of the new nodes 33 | function insertMultiple_unchecked(address[] calldata newNodes, uint64[] calldata balances) external; 34 | 35 | /// @notice timestamp when the selector starts 36 | function START_TIME() external view returns(uint256); 37 | 38 | /// @notice length of epoch 39 | function EPOCH_LENGTH() external view returns(uint256); 40 | 41 | /// @notice no of cluster selected in an epoch 42 | function NUMBER_OF_CLUSTERS_TO_SELECT() external view returns(uint256); 43 | 44 | /// @notice Current Epoch 45 | function getCurrentEpoch() external view returns (uint256); 46 | 47 | /// @notice Clusters are selected only for next epoch in this epoch using selectClusters method. 48 | /// If the method is not called within the previous epoch, then the last selected clusters 49 | /// are considered as selected for this epoch 50 | /// @param epoch Epoch Number 51 | function getClusters(uint256 epoch) external view returns (address[] memory clusters); 52 | 53 | /// @notice Clusters are selected only for next epoch in this epoch using selectClusters method. 54 | /// If the method is not called within the previous epoch, then the last selected clusters 55 | /// are considered as selected for this epoch 56 | /// @param from Epoch Number 57 | /// @param to Epoch Number 58 | function getClustersRanged(uint256 from, uint256 to) external view returns (address[][] memory clusters); 59 | 60 | /// @notice Returns the list of selected clusters for the next 61 | /// @return nodes List of the clusters selected 62 | function selectClusters() external returns (address[] memory nodes); 63 | 64 | /// @notice Delete a node from tree if it is stored 65 | /// @param key Address of the node 66 | function deleteIfPresent(address key) external; 67 | 68 | /// @notice Update the number of clusters to select 69 | /// @param numberOfClusters New number of clusters to select 70 | function updateNumberOfClustersToSelect(uint256 numberOfClusters) external; 71 | } 72 | -------------------------------------------------------------------------------- /deployments/enclaves/MarketV1.ts: -------------------------------------------------------------------------------- 1 | import { ethers, upgrades, run } from 'hardhat'; 2 | import { Contract } from 'ethers'; 3 | import * as fs from 'fs'; 4 | import { MarketV1 } from "../../typechain-types"; 5 | import { upgrade as upgradeUtil } from '../utils/Upgrade'; 6 | const config = require('./config'); 7 | 8 | export async function deploy(paymentToken?: string, lockSelectors?: string[], lockWaitTimes?: number[], noLog?: boolean): Promise { 9 | let chainId = (await ethers.provider.getNetwork()).chainId; 10 | 11 | const chainConfig = config[chainId]; 12 | 13 | const MarketV1 = await ethers.getContractFactory('MarketV1'); 14 | 15 | var addresses: { [key: string]: { [key: string]: string } } = {}; 16 | if (!noLog) { 17 | console.log("Chain Id:", chainId); 18 | } 19 | 20 | if (fs.existsSync('address.json')) { 21 | addresses = JSON.parse(fs.readFileSync('address.json', 'utf8')); 22 | } 23 | 24 | if (addresses[chainId] === undefined) { 25 | addresses[chainId] = {}; 26 | } 27 | 28 | if (addresses[chainId]['MarketV1'] !== undefined) { 29 | if (!noLog) console.log("Existing deployment:", addresses[chainId]['MarketV1']); 30 | return MarketV1.attach(addresses[chainId]['MarketV1']); 31 | } 32 | 33 | if (paymentToken === undefined) { 34 | paymentToken = addresses[chainId][chainConfig.enclaves.paymentToken]; 35 | if (paymentToken === undefined) { 36 | if (chainConfig.enclaves.paymentToken.startsWith("0x")) { 37 | paymentToken = chainConfig.enclaves.paymentToken; 38 | } else { 39 | throw new Error("Payment token unavailable"); 40 | } 41 | } 42 | } 43 | 44 | if (lockSelectors === undefined && lockWaitTimes === undefined) { 45 | lockSelectors = chainConfig.enclaves.lockWaitTimes.map((a: any) => a.selector); 46 | lockWaitTimes = chainConfig.enclaves.lockWaitTimes.map((a: any) => a.time); 47 | } 48 | 49 | if (lockSelectors?.length != lockWaitTimes?.length) { 50 | throw new Error("lockSelectors and lockWaitTimes not matching lengths"); 51 | } 52 | 53 | let admin = chainConfig.admin; 54 | 55 | let marketV1 = await upgrades.deployProxy(MarketV1, [admin, paymentToken, lockSelectors, lockWaitTimes], { kind: "uups" }); 56 | 57 | if (!noLog) { 58 | console.log("Deployed addr:", marketV1.address); 59 | 60 | addresses[chainId]['MarketV1'] = marketV1.address; 61 | 62 | fs.writeFileSync('address.json', JSON.stringify(addresses, null, 2), 'utf8'); 63 | } 64 | 65 | return marketV1; 66 | } 67 | 68 | export async function upgrade() { 69 | await upgradeUtil('MarketV1', 'MarketV1', []); 70 | } 71 | 72 | export async function verify() { 73 | let chainId = (await ethers.provider.getNetwork()).chainId; 74 | console.log("Chain Id:", chainId); 75 | 76 | var addresses: { [key: string]: { [key: string]: string } } = {}; 77 | if (fs.existsSync('address.json')) { 78 | addresses = JSON.parse(fs.readFileSync('address.json', 'utf8')); 79 | } 80 | 81 | if (addresses[chainId] === undefined || addresses[chainId]['MarketV1'] === undefined) { 82 | throw new Error("MarketV1 not deployed"); 83 | } 84 | 85 | const implAddress = await upgrades.erc1967.getImplementationAddress(addresses[chainId]['MarketV1']); 86 | 87 | await run("verify:verify", { 88 | address: implAddress, 89 | constructorArguments: [] 90 | }); 91 | 92 | console.log("MarketV1 verified"); 93 | } 94 | -------------------------------------------------------------------------------- /benchmarks/ClusterSelector.ts: -------------------------------------------------------------------------------- 1 | import { ethers } from "hardhat"; 2 | import { loadFixture } from '@nomicfoundation/hardhat-network-helpers' 3 | import { benchmark as benchmarkDeployment } from "./helpers/deployment"; 4 | import { initDataFixture } from "./fixtures/ClusterSelector"; 5 | import { BigNumber, BigNumberish, constants, PopulatedTransaction, Signer, utils } from "ethers"; 6 | import { randomlyDivideInXPieces, skipTime } from "./helpers/util"; 7 | import { MockContract } from "@ethereum-waffle/mock-contract"; 8 | import { FuzzedNumber } from "../utils/fuzzer"; 9 | import { ArbGasInfo__factory, ClusterSelector } from "../typechain-types"; 10 | 11 | const estimator = new ethers.Contract("0x000000000000000000000000000000000000006c", [ 12 | "function getPricesInArbGas() view returns(uint256 gasPerL2Tx, uint256 gasPerL1CallDataByte, uint256)" 13 | ]); 14 | const mainnetProvider = new ethers.providers.JsonRpcProvider("https://arb1.arbitrum.io/rpc"); 15 | 16 | describe("Cluster Rewards", async () => { 17 | const arbGasInfo = await new ArbGasInfo__factory().deploy() 18 | const gasResult = await estimator.getPricesInArbGas() 19 | await arbGasInfo.setPrices(gasResult[0], gasResult[1], gasResult[2]) 20 | 21 | benchmarkDeployment('ClusterSelector', [parseInt(Date.now()/1000 + ""), 900, arbGasInfo.address, 1000, 200000], [ 22 | "0x000000000000000000000000000000000000dEaD", 23 | "0x000000000000000000000000000000000000dEaD", 24 | ]); 25 | 26 | describe("Select Clusters", async () => { 27 | let arbGasInfoMock: MockContract; 28 | let clusterSelector: ClusterSelector; 29 | let admin: Signer; 30 | let rewardDelegatorsMock: Signer; 31 | let nodesInserted: number; 32 | let EPOCH_LENGTH: BigNumber; 33 | let l1GasDetails: any; 34 | 35 | beforeEach(async function() { 36 | this.timeout(1000000); 37 | process.env.NO_OF_CLUSTERS = "200"; 38 | ({ 39 | arbGasInfoMock, 40 | clusterSelector, 41 | admin, 42 | rewardDelegatorsMock, 43 | nodesInserted, 44 | } = await loadFixture(initDataFixture)); 45 | 46 | l1GasDetails = await estimator.connect(mainnetProvider).getPricesInArbGas(); 47 | EPOCH_LENGTH = await clusterSelector.EPOCH_LENGTH(); 48 | console.log("********************config***********************") 49 | console.log(`Clusters Inserted: ${nodesInserted}`); 50 | console.log(`L1 Gas details, Base L1 gas/tx: ${l1GasDetails[0]}, L1 Gas/calldataByte: ${l1GasDetails[1]} `); 51 | console.log("*************************************************") 52 | }); 53 | 54 | it("Select clusters 1000 times", async function() { 55 | this.timeout(1000000); 56 | const iterations = 1000; 57 | let totalGas = BigNumber.from(0); 58 | let maxGas = BigNumber.from(0); 59 | 60 | console.log(`No of iterations: ${iterations}`); 61 | 62 | for(let i=0; i < iterations; i++) { 63 | await skipTime(FuzzedNumber.randomInRange(EPOCH_LENGTH, EPOCH_LENGTH.mul(3)).toNumber()); 64 | const tx = await clusterSelector.selectClusters({gasLimit: 1000000}); 65 | const receipt = await tx.wait(); 66 | totalGas = totalGas.add(receipt.gasUsed); 67 | if(maxGas.lt(receipt.gasUsed)) maxGas = receipt.gasUsed; 68 | } 69 | 70 | console.log(`Average gas used: ${totalGas.div(iterations).toNumber()}`); 71 | console.log(`Max gas used: ${maxGas.toNumber()}`); 72 | }); 73 | }) 74 | }); -------------------------------------------------------------------------------- /test/governance/GovernorAlpha.ts: -------------------------------------------------------------------------------- 1 | import { ethers, upgrades } from "hardhat"; 2 | import { expect, util } from "chai"; 3 | import { BigNumber as BN, Signer } from "ethers"; 4 | import { MPond, Timelock } from "../../typechain-types"; 5 | import { getGovernorAlpha, getMpond, getTimelock } from "../../utils/typechainConvertor"; 6 | 7 | declare module "ethers" { 8 | interface BigNumber { 9 | e18(this: BigNumber): BigNumber; 10 | } 11 | } 12 | 13 | BN.prototype.e18 = function () { 14 | return this.mul(BN.from(10).pow(18)); 15 | }; 16 | 17 | describe("GovernorAlpha", function () { 18 | let signers: Signer[]; 19 | let addrs: string[]; 20 | let mpond: MPond; 21 | let timelock: Timelock; 22 | 23 | beforeEach(async function () { 24 | signers = await ethers.getSigners(); 25 | addrs = await Promise.all(signers.map((a) => a.getAddress())); 26 | 27 | const MPond = await ethers.getContractFactory("MPond"); 28 | let mpondContract = await upgrades.deployProxy(MPond, { kind: "uups" }); 29 | mpond = getMpond(mpondContract.address, signers[0]); 30 | 31 | const Timelock = await ethers.getContractFactory("Timelock"); 32 | let timelockContract = await upgrades.deployProxy(Timelock, [2 * 24 * 60 * 60], { kind: "uups" }); 33 | timelock = getTimelock(timelockContract.address, signers[0]); 34 | }); 35 | 36 | it("deploys with initialization disabled", async function () { 37 | const GovernorAlpha = await ethers.getContractFactory("GovernorAlpha"); 38 | let governorAlphaContract = await GovernorAlpha.deploy(); 39 | let governorAlpha = getGovernorAlpha(governorAlphaContract.address, signers[0]); 40 | await expect(governorAlpha.initialize(timelock.address, mpond.address, addrs[1])).to.be.reverted; 41 | }); 42 | 43 | it("deploys as proxy and initializes", async function () { 44 | const GovernorAlpha = await ethers.getContractFactory("GovernorAlpha"); 45 | let governorAlphaContract = await upgrades.deployProxy(GovernorAlpha, [timelock.address, mpond.address, addrs[1]], { kind: "uups" }); 46 | let governorAlpha = getGovernorAlpha(governorAlphaContract.address, signers[0]); 47 | 48 | expect(await governorAlpha.timelock()).to.equal(timelock.address); 49 | expect(await governorAlpha.MPond()).to.equal(mpond.address); 50 | expect(await governorAlpha.hasRole(await governorAlpha.DEFAULT_ADMIN_ROLE(), addrs[0])).to.be.true; 51 | expect(await governorAlpha.guardian()).to.equal(addrs[1]); 52 | }); 53 | 54 | it("upgrades", async function () { 55 | const GovernorAlpha = await ethers.getContractFactory("GovernorAlpha"); 56 | let governorAlphaContract = await upgrades.deployProxy(GovernorAlpha, [timelock.address, mpond.address, addrs[1]], { kind: "uups" }); 57 | let governorAlpha = getGovernorAlpha(governorAlphaContract.address, signers[0]); 58 | await upgrades.upgradeProxy(governorAlpha.address, GovernorAlpha, { kind: "uups" }); 59 | 60 | expect(await governorAlpha.timelock()).to.equal(timelock.address); 61 | expect(await governorAlpha.MPond()).to.equal(mpond.address); 62 | expect(await governorAlpha.hasRole(await governorAlpha.DEFAULT_ADMIN_ROLE(), addrs[0])).to.be.true; 63 | expect(await governorAlpha.guardian()).to.equal(addrs[1]); 64 | }); 65 | 66 | it("does not upgrade without admin", async function () { 67 | const GovernorAlpha = await ethers.getContractFactory("GovernorAlpha"); 68 | let governorAlphaContract = await upgrades.deployProxy(GovernorAlpha, [timelock.address, mpond.address, addrs[1]], { kind: "uups" }); 69 | let governorAlpha = getGovernorAlpha(governorAlphaContract.address, signers[0]); 70 | 71 | await expect(upgrades.upgradeProxy(governorAlpha.address, GovernorAlpha.connect(signers[1]), { kind: "uups" })).to.be.reverted; 72 | }); 73 | }); 74 | -------------------------------------------------------------------------------- /deployments/staking/ReceiverStaking.ts: -------------------------------------------------------------------------------- 1 | import { ethers, run, upgrades } from 'hardhat'; 2 | import { Contract } from 'ethers'; 3 | import * as fs from 'fs'; 4 | import { upgrade as upgradeUtil } from '../utils/Upgrade'; 5 | const config = require('./config'); 6 | 7 | 8 | export async function deploy(admin?: string, startTime?: number, epochLength?: number, stakingToken?: string, noLog?: boolean): Promise { 9 | let chainId = (await ethers.provider.getNetwork()).chainId; 10 | 11 | const chainConfig = config[chainId]; 12 | 13 | const ReceiverStaking = await ethers.getContractFactory('ReceiverStaking'); 14 | 15 | var addresses: {[key: string]: {[key: string]: string}} = {}; 16 | if(!noLog) { 17 | console.log("Chain Id:", chainId); 18 | 19 | if(fs.existsSync('address.json')) { 20 | addresses = JSON.parse(fs.readFileSync('address.json', 'utf8')); 21 | } 22 | 23 | if(addresses[chainId] === undefined) { 24 | addresses[chainId] = {}; 25 | } 26 | 27 | if(addresses[chainId]['ReceiverStaking'] !== undefined) { 28 | console.log("Existing deployment:", addresses[chainId]['ReceiverStaking']); 29 | return ReceiverStaking.attach(addresses[chainId]['ReceiverStaking']); 30 | } 31 | } 32 | 33 | let signers = await ethers.getSigners(); 34 | 35 | if(admin == undefined) admin = chainConfig.admin; 36 | if(startTime == undefined) startTime = chainConfig.startTime; 37 | if(epochLength == undefined) epochLength = chainConfig.epochLength; 38 | if(stakingToken == undefined) stakingToken = chainConfig.staking.receiver.token; 39 | 40 | let receiverStaking = await upgrades.deployProxy(ReceiverStaking, [ admin, "Receiver POND", "rPOND" ], { 41 | kind: "uups", 42 | constructorArgs: [ 43 | startTime, 44 | epochLength, 45 | stakingToken 46 | ] 47 | }); 48 | 49 | if(!noLog) { 50 | console.log("Deployed addr:", receiverStaking.address); 51 | 52 | addresses[chainId]['ReceiverStaking'] = receiverStaking.address; 53 | 54 | fs.writeFileSync('address.json', JSON.stringify(addresses, null, 2), 'utf8'); 55 | } 56 | 57 | return receiverStaking; 58 | } 59 | 60 | export async function upgrade(startTime?: number, epochLength?: number, stakingToken?: string) { 61 | let chainId = (await ethers.provider.getNetwork()).chainId 62 | const chainConfig = config[chainId]; 63 | 64 | if(startTime == undefined) startTime = chainConfig.startTime; 65 | if(epochLength == undefined) epochLength = chainConfig.epochLength; 66 | if(stakingToken == undefined) stakingToken = chainConfig.staking.receiver.token; 67 | 68 | await upgradeUtil('ReceiverStaking', 'ReceiverStaking', [ 69 | startTime, epochLength, stakingToken 70 | ]); 71 | } 72 | 73 | export async function verify() { 74 | let chainId = (await ethers.provider.getNetwork()).chainId; 75 | console.log("Chain Id:", chainId); 76 | 77 | const chainConfig = config[chainId]; 78 | 79 | var addresses: {[key: string]: {[key: string]: string}} = {}; 80 | if(fs.existsSync('address.json')) { 81 | addresses = JSON.parse(fs.readFileSync('address.json', 'utf8')); 82 | } 83 | 84 | if(addresses[chainId] === undefined || addresses[chainId]['ReceiverStaking'] === undefined) { 85 | throw new Error("Receiver Staking not deployed"); 86 | } 87 | 88 | const implAddress = await upgrades.erc1967.getImplementationAddress(addresses[chainId]['ReceiverStaking']); 89 | 90 | await run("verify:verify", { 91 | address: implAddress, 92 | constructorArguments: [ 93 | chainConfig.startTime, 94 | chainConfig.epochLength, 95 | chainConfig.staking.receiver.token 96 | ] 97 | }); 98 | 99 | console.log("Receiver Staking verified"); 100 | } -------------------------------------------------------------------------------- /deployments/tokens/mpond.js: -------------------------------------------------------------------------------- 1 | const networks = require("../networks"); 2 | const deployContract = require("../utils/helpers"); 3 | 4 | if (!networks[process.env.NETWORK]) { 5 | console.log("NETWORK not declared or valid in env variables"); 6 | process.exit(1); 7 | } else { 8 | console.log(`Deploying contracts on ${networks[process.env.NETWORK]}`); 9 | } 10 | 11 | const Web3 = require("web3"); 12 | const web3 = new Web3(networks[process.env.NETWORK]); 13 | 14 | var proxyAdmin; 15 | 16 | if (!process.env.PRIV_KEY) { 17 | console.log("PRIV_KEY not defined in env variables"); 18 | } else { 19 | web3.eth.accounts.wallet.add(process.env.PRIV_KEY); 20 | } 21 | 22 | if (!process.env.PROXY_ADMIN) { 23 | console.log("PROXY_ADMIN is not defined"); 24 | process.exit(1); 25 | } else { 26 | proxyAdmin = process.env.PROXY_ADMIN; 27 | } 28 | 29 | const config = { 30 | deploymentConfig: { 31 | gas: 5000000, 32 | gasPrice: 158000000000, 33 | from: web3.eth.accounts.wallet[0].address, 34 | }, 35 | }; 36 | 37 | const MPondLogic = require("../build/contracts/MPondLogic.json"); 38 | const MPondProxy = require("../build/contracts/MPondProxy.json"); 39 | 40 | async function deploy() { 41 | const MPondLogicAddress = await deployContract( 42 | web3, 43 | MPondLogic.abi, 44 | MPondLogic.bytecode, 45 | [], 46 | config.deploymentConfig 47 | ); 48 | 49 | // const MPondProxyAddress = await deployContract( 50 | // web3, 51 | // MPondProxy.abi, 52 | // MPondProxy.bytecode, 53 | // [MPondLogicAddress, proxyAdmin], 54 | // config.deploymentConfig 55 | // ); 56 | const MPondProxyAddress = "Already Exists"; 57 | return { 58 | MPondLogicAddress, 59 | MPondProxyAddress, 60 | }; 61 | } 62 | 63 | const dropBridgeAddress = "0x40ec5B33f54e0E8A33A975908C5BA1c14e5BbbDf"; 64 | 65 | async function init() { 66 | var tokenAddress = "0x28C29ED8D3288d20719F828D75b1445D1aE4DF84"; 67 | var tokenInstance = new web3.eth.Contract(MPondLogic.abi, tokenAddress); 68 | let result = await tokenInstance.methods 69 | .initialize( 70 | "0x0D53E7EB879B5318981a8377f5D459891e2503c5", 71 | "0xAE60C5F7D4720108813D4843F487F47439c4a5F4", 72 | dropBridgeAddress 73 | ) 74 | .send(config.deploymentConfig); 75 | return result; 76 | } 77 | 78 | // init().then(console.log); 79 | deploy().then(console.table).catch(console.log); 80 | 81 | // Matic 82 | // ┌───────────────────┬──────────────────────────────────────────────┐ 83 | // │ (index) │ Values │ 84 | // ├───────────────────┼──────────────────────────────────────────────┤ 85 | // │ MPondLogicAddress │ '0xf8888C42041bfe5b73066f0B4A45781d36D91253' │ 86 | // │ MPondProxyAddress │ '0x8B9D1Ce9769b6182988aa14a8013860315Db4513' │ 87 | // └───────────────────┴──────────────────────────────────────────────┘ 88 | 89 | // ETH Mainnet 90 | // ┌───────────────────┬──────────────────────────────────────────────┐ 91 | // │ (index) │ Values │ 92 | // ├───────────────────┼──────────────────────────────────────────────┤ 93 | // │ MPondLogicAddress │ '0xf7F42f8F26C84898F8a32590dA797c94Af06104c' │ 94 | // │ MPondProxyAddress │ '0x7e88Aeaef1163bc6fA1D30cC360a3D5388e161ab' │ 95 | // └───────────────────┴──────────────────────────────────────────────┘ 96 | 97 | // ERC677BridgeToken 98 | // 0xF9c7483851017744FE0A5D79607AC609619B9eA7 99 | 100 | // copies 101 | // eth-mpond, matic-mpond 102 | // 0xB5dC1Ea91467adF8a6428063e29Cd83eF9DF1e11, 0xD2A4044881b46BfAc19ed1c73A0F3f277e5A6bA5 103 | // 0x11DB7A790738a51f29809ff9416282677F8495C1, 0x4DE15c28b262356050E2Db74aA7f076B7a136337 104 | // 0x28C29ED8D3288d20719F828D75b1445D1aE4DF84, 0x240C82Fdd68c7991e77f4222dA2A9Ef9a7AaFA67 105 | -------------------------------------------------------------------------------- /address.json: -------------------------------------------------------------------------------- 1 | { 2 | "1": { 3 | "Pond": "0x57b946008913b82e4df85f501cbaed910e58d26c", 4 | "MPond": "0x1c77d15857646687005dbbafff5873f4495a9731", 5 | "PondGateway": "0xeAd69Ec22350E45B30a468F55AE03b9998de52AC", 6 | "MPondGateway": "0x02520e5cfA75DfEe7b0B51A658Bf4a7675eDf253" 7 | }, 8 | "4": { 9 | "Pond": "0xA8dEA78063C01D904adEF50F5483426d5c5aD512", 10 | "MPond": "0x8a065c607dbc09CD753D4c73552314F2d52140D8", 11 | "PondGateway": "0x05aD26750F777EbA9bb523Aeb688C2438df080E5", 12 | "MPondGateway": "0x4F937970082771355B3Aab3d171Cb8FaA9F28EE8" 13 | }, 14 | "137": { 15 | "MarketV1": "0x4dA7A531EF660533074341df098F722F1Aa5dFaa", 16 | "AttestationVerifier": "0xDEb1326Bf357FA5BfBf0632dF7b6E338d817500D" 17 | }, 18 | "250": { 19 | "Pond": "0x8Fbf3759cDe693B19493663a4ef5853728Cb71f8" 20 | }, 21 | "42161": { 22 | "Pond": "0xdA0a57B710768ae17941a9Fa33f8B720c8bD9ddD", 23 | "MPond": "0xC606157CdBEb8e0BDB273E40D6Ee96e151083194", 24 | "ClusterRegistry": "0x1f3Fe1E2a752cF6732C8be849a424482639EffC9", 25 | "RewardDelegators": "0xfB1F3fFa0d9819Da45D4E91967D46fAF025aa1c3", 26 | "ClusterRewards": "0xc2033B3Ea8C226461ac7408BA948806F09148788", 27 | "StakeManager": "0xf90490186F370f324DEF2871F077668455f65253", 28 | "MPondGateway": "0xbF6130Dc424332AaE55709DB39638EcA25F0EB5b", 29 | "PondGateway": "0x6727C909ee7d87CD6E60116f9B7363eC4613A27a", 30 | "Bridge": "0x7Cf89b836d1C647Ef31a5eeb152B7aDDCf002a08", 31 | "ReceiverStaking": "0x298222e9f54396e70EE1EB2FB58aad4e7Da1f878", 32 | "ClusterSelector_ETH": "0xf1E4c2d4B2996C7c34b5a6136861D7385Cb0E843", 33 | "MarketV1": "0x9d95D61eA056721E358BC49fE995caBF3B86A34B" 34 | }, 35 | "59144": { 36 | "MarketV1": "0xdD78e7F31B98dD8BD1F44F8FFCCeb4EdA6abdc5B" 37 | }, 38 | "421611": { 39 | "MPond": "0x20BC113bEabB340Ed8Ba7926969192A1Ca489ddC", 40 | "Pond": "0xF727E7ACAa95Be373320A3DEf33876fEa34232bF", 41 | "PondGateway": "0x517E2F0E4585D69803eBF6679CC7819f8c3B7a17", 42 | "MPondGateway": "0xd8f387AbAcc286B79F1A1D21D13e95AE7655A044", 43 | "ClusterRegistry": "0x80B45D7A385f2b2D8433877D36B01AB5C1892bAb", 44 | "RewardDelegators": "0x2ea3Ba8e1564b2925579e864D6F65606293DcdB4", 45 | "ClusterRewards": "0x6ffACC200e0dCb607b9Be64077eb0f8eEc00e73a", 46 | "StakeManager": "0x0EC28b2250574c18839E6723a276A2A89D9E5a50" 47 | }, 48 | "421613": { 49 | "Pond": "0xa9472a9C135Cb33221de441a4DEd393FD515a69C", 50 | "MarketV1": "0x29cbA3c69CB4Cf89a4e8b5f698df7C607A488e4f", 51 | "MPond": "0x0B3d9b224496C2A2Fa1a4096D8EB4D350eFd9079", 52 | "Bridge": "0xfeEa9a34e51e4E90b8B62F38120A345650164110", 53 | "RewardDelegators": "0xe62CC1181Fb74c1954f13246b6863A8E26a0D77d", 54 | "StakeManager": "0x03040082dc6d47cd9fa4AD474D6d821FD6F0A04C", 55 | "ClusterRegistry": "0x1fe9f98C4c0eC29a012f8B8fFDe962a13fCECe1E", 56 | "ClusterSelector_ETH": "0x9c17478fC7300AB513111d93455Bee5105792FB4", 57 | "ReceiverStaking": "0x49fB661B8Cd1A76DEF8E11eB6d16e7c61C2C9270", 58 | "ClusterRewards": "0xf0ac76EB619DA28ed6907965C17eDbFd16B15CE0" 59 | }, 60 | "421614": { 61 | "AttestationVerifier": "0x777c32858660B3B38D54216c43589A74a4896220", 62 | "Pond": "0x8995673cef56d4cBAE3cE96157a8fc977D4f8525", 63 | "MPond": "0x071BaFF53B0Ca54E4aec59Cda64f9cC5a7205e2c", 64 | "RewardDelegators": "0x09c7369e72726dBE24C34e1822D38cC919b793DC", 65 | "StakeManager": "0x464d85D3a29A52F15886376823D56aB5D37619AB", 66 | "ClusterRegistry": "0xdcbF33e2e3BAdD7B6Afc8D677175a66D63bE6d9A", 67 | "ClusterSelector_ETH": "0xdABc7d755B0450c0759b5641Ac5111D7073da6DF", 68 | "ReceiverStaking": "0x2f2Ee222bfe110E37C4F7b84A542fF53CE8e5C77", 69 | "ClusterRewards": "0x0266E6D433466D0ff153A4F853bF9C7516512E13", 70 | "MarketV1": "0xDcD2846DCA523Db1C8F3c842a41A58099dE26A0A", 71 | "Bridge": "0x1bC0EFc6cd42884EB527228b0A41AcaEB5FdEB27" 72 | } 73 | } -------------------------------------------------------------------------------- /utils/typechainConvertor.ts: -------------------------------------------------------------------------------- 1 | import { Signer } from "ethers"; 2 | import { 3 | MPond__factory, 4 | Pond__factory, 5 | MPond, 6 | Pond, 7 | Bridge, 8 | Bridge__factory, 9 | Timelock, 10 | Timelock__factory, 11 | GovernorAlpha, 12 | GovernorAlpha__factory, 13 | ReceiverStaking, 14 | ReceiverStaking__factory, 15 | ClusterRegistry, 16 | ClusterRegistry__factory, 17 | RewardDelegators, 18 | RewardDelegators__factory, 19 | StakeManager, 20 | StakeManager__factory, 21 | ClusterRewards, 22 | ClusterRewards__factory, 23 | ClusterSelector, 24 | ClusterSelector__factory, 25 | MarketV1, 26 | MarketV1__factory, 27 | AttestationVerifier__factory, 28 | AttestationVerifier, 29 | AttestationAutherUpgradeable__factory, 30 | AttestationAutherUpgradeable, 31 | AttestationAutherSample__factory, 32 | AttestationAutherSample, 33 | Credit__factory, 34 | Credit, 35 | } from "../typechain-types"; 36 | 37 | export function getMpond(contractAddress: string, signer: Signer): MPond { 38 | return new MPond__factory(signer).attach(contractAddress); 39 | } 40 | 41 | export function getPond(contractAddress: string, signer: Signer): Pond { 42 | return new Pond__factory(signer).attach(contractAddress); 43 | } 44 | 45 | export function getBridge(contractAddress: string, signer: Signer): Bridge { 46 | return new Bridge__factory(signer).attach(contractAddress); 47 | } 48 | 49 | export function getTimelock(contractAddress: string, signer: Signer): Timelock { 50 | return new Timelock__factory(signer).attach(contractAddress); 51 | } 52 | 53 | export function getGovernorAlpha(contractAddress: string, signer: Signer): GovernorAlpha { 54 | return new GovernorAlpha__factory(signer).attach(contractAddress); 55 | } 56 | 57 | export function getReceiverStaking(contractAddress: string, signer: Signer): ReceiverStaking { 58 | return new ReceiverStaking__factory(signer).attach(contractAddress); 59 | } 60 | 61 | export function getClusterRegistry(contractAddress: string, signer: Signer): ClusterRegistry { 62 | return new ClusterRegistry__factory(signer).attach(contractAddress); 63 | } 64 | 65 | export function getRewardDelegators(contractAddress: string, signer: Signer): RewardDelegators { 66 | return new RewardDelegators__factory(signer).attach(contractAddress); 67 | } 68 | 69 | export function getStakeManager(contractAddress: string, signer: Signer): StakeManager { 70 | return new StakeManager__factory(signer).attach(contractAddress); 71 | } 72 | 73 | export function getClusterSelector(contractAddress: string, signer: Signer): ClusterSelector { 74 | return new ClusterSelector__factory(signer).attach(contractAddress); 75 | } 76 | 77 | export function getClusterRewards(contractAddress: string, signer: Signer): ClusterRewards { 78 | return new ClusterRewards__factory(signer).attach(contractAddress); 79 | } 80 | 81 | export function getCredit(contractAddress: string, signer: Signer): Credit { 82 | return new Credit__factory(signer).attach(contractAddress); 83 | } 84 | 85 | export function getMarketV1(contractAddress: string, signer: Signer): MarketV1 { 86 | return new MarketV1__factory(signer).attach(contractAddress); 87 | } 88 | 89 | export function getAttestationVerifier(contractAddress: string, signer: Signer): AttestationVerifier { 90 | return new AttestationVerifier__factory(signer).attach(contractAddress); 91 | } 92 | 93 | export function getAttestationAutherUpgradeable(contractAddress: string, signer: Signer): AttestationAutherUpgradeable { 94 | return new AttestationAutherUpgradeable__factory(signer).attach(contractAddress); 95 | } 96 | 97 | export function getAttestationAutherSample(contractAddress: string, signer: Signer): AttestationAutherSample { 98 | return new AttestationAutherSample__factory(signer).attach(contractAddress); 99 | } 100 | -------------------------------------------------------------------------------- /hardhat.config.ts: -------------------------------------------------------------------------------- 1 | import '@typechain/hardhat'; 2 | import '@nomiclabs/hardhat-ethers'; 3 | import "@nomiclabs/hardhat-etherscan"; 4 | import '@openzeppelin/hardhat-upgrades'; 5 | import 'solidity-coverage'; 6 | import "hardhat-gas-reporter"; 7 | import "@nomicfoundation/hardhat-chai-matchers"; 8 | import "hardhat-tracer"; 9 | // import * as tdly from "@tenderly/hardhat-tenderly"; 10 | // tdly.setup(); 11 | 12 | import dotenv from 'dotenv'; 13 | 14 | dotenv.config(); 15 | 16 | export default { 17 | networks: { 18 | hardhat: { 19 | accounts: { 20 | count: 500 21 | }, 22 | allowBlocksWithSameTimestamp: true, 23 | }, 24 | eth: { 25 | url: `https://mainnet.infura.io/v3/${process.env.INFURA_API_KEY}`, 26 | accounts: process.env.ETH_DEPLOYER_KEY !== undefined ? [process.env.ETH_DEPLOYER_KEY] : undefined, 27 | }, 28 | arb1: { 29 | url: "https://arb1.arbitrum.io/rpc", 30 | accounts: process.env.ARBITRUM_DEPLOYER_KEY !== undefined ? [process.env.ARBITRUM_DEPLOYER_KEY] : undefined, 31 | }, 32 | goerli: { 33 | url: "https://rpc.goerli.dev", 34 | accounts: process.env.GOERLI_DEPLOYER_KEY !== undefined ? [process.env.GOERLI_DEPLOYER_KEY] : undefined, 35 | }, 36 | arbg: { 37 | url: "https://goerli-rollup.arbitrum.io/rpc", 38 | accounts: process.env.ARBITRUM_GOERLI_DEPLOYER_KEY !== undefined ? [process.env.ARBITRUM_GOERLI_DEPLOYER_KEY] : undefined, 39 | }, 40 | arbs: { 41 | url: "https://sepolia-rollup.arbitrum.io/rpc", 42 | accounts: process.env.ARBITRUM_SEPOLIA_DEPLOYER_KEY !== undefined ? [process.env.ARBITRUM_SEPOLIA_DEPLOYER_KEY] : undefined, 43 | }, 44 | linea: { 45 | url: "https://rpc.linea.build", 46 | }, 47 | polygon: { 48 | url: "https://polygon-rpc.com/", 49 | }, 50 | }, 51 | solidity: { 52 | compilers: [ 53 | { 54 | version: "0.8.17", 55 | settings: { 56 | // viaIR: true, 57 | optimizer: { 58 | enabled: true, 59 | runs: 10000, 60 | }, 61 | } 62 | }, 63 | { 64 | version: "0.8.26", 65 | settings: { 66 | viaIR: true, 67 | optimizer: { 68 | enabled: true, 69 | runs: 10000, 70 | }, 71 | } 72 | } 73 | ], 74 | // tried to use SMTChecker, gets killed, investigate later 75 | // modelChecker: { 76 | // engine: "all", 77 | // targets: [ 78 | // "assert", 79 | // "overflow", 80 | // "underflow", 81 | // "divByZero", 82 | // "constantCondition", 83 | // "popEmptyArray", 84 | // "outOfBounds", 85 | // "balance", 86 | // ], 87 | // }, 88 | }, 89 | etherscan: { 90 | apiKey: { 91 | mainnet: process.env.ETHERSCAN_API_KEY, 92 | arb1: process.env.ARBISCAN_API_KEY, 93 | arbg: process.env.ARBISCAN_API_KEY, 94 | arbs: process.env.ARBISCAN_API_KEY, 95 | linea: process.env.LINEASCAN_API_KEY, 96 | polygon: process.env.POLYGONSCAN_API_KEY, 97 | }, 98 | customChains: [{ 99 | network: "arbg", 100 | chainId: 421613, 101 | urls: { 102 | apiURL: "https://api-goerli.arbiscan.io/api", 103 | browserURL: "https://goerli.arbiscan.io", 104 | }, 105 | }, { 106 | network: "arbs", 107 | chainId: 421614, 108 | urls: { 109 | apiURL: "https://api-sepolia.arbiscan.io/api", 110 | browserURL: "https://sepolia.arbiscan.io", 111 | }, 112 | }, { 113 | network: "arb1", 114 | chainId: 42161, 115 | urls: { 116 | apiURL: "https://api.arbiscan.io/api", 117 | browserURL: "https://arbiscan.io", 118 | }, 119 | }, { 120 | network: "linea", 121 | chainId: 59144, 122 | urls: { 123 | apiURL: "https://api.lineascan.build/api", 124 | browserURL: "https://lineascan.build", 125 | }, 126 | }], 127 | }, 128 | gasReporter: { 129 | enabled: process.env?.GAS_REPORTER?.toLowerCase() == "true" 130 | } 131 | }; 132 | 133 | -------------------------------------------------------------------------------- /contracts/lock/LockUpgradeable.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; 6 | import "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol"; 7 | 8 | 9 | contract LockUpgradeable is 10 | Initializable, // initializer 11 | ContextUpgradeable // _msgSender, _msgData 12 | { 13 | struct Lock { 14 | uint256 unlockTime; 15 | uint256 iValue; 16 | } 17 | 18 | mapping(bytes32 => Lock) public locks; 19 | mapping(bytes32 => uint256) public lockWaitTime; 20 | 21 | uint256[48] private __gap; 22 | 23 | enum LockStatus { 24 | None, 25 | Unlocked, 26 | Locked 27 | } 28 | 29 | event LockWaitTimeUpdated(bytes32 indexed selector, uint256 prevLockTime, uint256 updatedLockTime); 30 | event LockCreated(bytes32 indexed selector, bytes32 indexed key, uint256 iValue, uint256 unlockTime); 31 | event LockDeleted(bytes32 indexed selector, bytes32 indexed key, uint256 iValue); 32 | 33 | function __Lock_init_unchained(bytes32[] memory _selectors, uint256[] memory _lockWaitTimes) internal onlyInitializing { 34 | _updateLockWaitTimes(_selectors, _lockWaitTimes); 35 | } 36 | 37 | function _lockStatus(bytes32 _selector, bytes32 _key) internal view returns (LockStatus) { 38 | bytes32 _lockId = keccak256(abi.encodePacked(_selector, _key)); 39 | uint256 _unlockTime = locks[_lockId].unlockTime; 40 | if(_unlockTime == 0) { 41 | return LockStatus.None; 42 | } else if(_unlockTime <= block.timestamp) { 43 | return LockStatus.Unlocked; 44 | } else { 45 | return LockStatus.Locked; 46 | } 47 | } 48 | 49 | function _lock(bytes32 _selector, bytes32 _key, uint256 _iValue) internal returns (uint256) { 50 | require(_lockStatus(_selector, _key) == LockStatus.None); 51 | 52 | uint256 _duration = lockWaitTime[_selector]; 53 | bytes32 _lockId = keccak256(abi.encodePacked(_selector, _key)); 54 | uint256 _unlockTime = block.timestamp + _duration; 55 | locks[_lockId].unlockTime = _unlockTime; 56 | locks[_lockId].iValue = _iValue; 57 | 58 | emit LockCreated(_selector, _key, _iValue, _unlockTime); 59 | 60 | return _unlockTime; 61 | } 62 | 63 | function _revertLock(bytes32 _selector, bytes32 _key) internal returns (uint256) { 64 | bytes32 _lockId = keccak256(abi.encodePacked(_selector, _key)); 65 | uint256 _iValue = locks[_lockId].iValue; 66 | delete locks[_lockId]; 67 | 68 | emit LockDeleted(_selector, _key, _iValue); 69 | 70 | return _iValue; 71 | } 72 | 73 | function _unlock(bytes32 _selector, bytes32 _key) internal returns (uint256) { 74 | require(_lockStatus(_selector, _key) == LockStatus.Unlocked); 75 | return _revertLock(_selector, _key); 76 | } 77 | 78 | function _cloneLock(bytes32 _selector, bytes32 _fromKey, bytes32 _toKey) internal { 79 | bytes32 _fromLockId = keccak256(abi.encodePacked(_selector, _fromKey)); 80 | bytes32 _toLockId = keccak256(abi.encodePacked(_selector, _toKey)); 81 | 82 | uint256 _unlockTime = locks[_fromLockId].unlockTime; 83 | uint256 _iValue = locks[_fromLockId].iValue; 84 | 85 | locks[_toLockId].unlockTime = _unlockTime; 86 | locks[_toLockId].iValue = _iValue; 87 | 88 | emit LockCreated(_selector, _toKey, _iValue, _unlockTime); 89 | } 90 | 91 | function _updateLockWaitTime(bytes32 _selector, uint256 _newLockWaitTime) internal { 92 | emit LockWaitTimeUpdated(_selector, lockWaitTime[_selector], _newLockWaitTime); 93 | lockWaitTime[_selector] = _newLockWaitTime; 94 | } 95 | 96 | function _updateLockWaitTimes(bytes32[] memory _selectors, uint256[] memory _newLockWaitTimes) internal { 97 | require(_selectors.length == _newLockWaitTimes.length, "Lock: length mismatch"); 98 | 99 | for(uint256 _i = 0; _i < _selectors.length; _i++) { 100 | _updateLockWaitTime(_selectors[_i], _newLockWaitTimes[_i]); 101 | } 102 | } 103 | } 104 | 105 | -------------------------------------------------------------------------------- /deployments/staking/ClusterSelector.ts: -------------------------------------------------------------------------------- 1 | import { ethers, run, upgrades } from "hardhat"; 2 | import { Contract } from "ethers"; 3 | import * as fs from "fs"; 4 | import { upgrade as upgradeUtil } from '../utils/Upgrade'; 5 | const config = require('./config'); 6 | 7 | export async function deploy(network: string, rewardDelegators: string, arbGasInfo?: string, admin?: string, startTime?: number, epochLength?: number, gasRefund?: string, maxReward?: string, noLog?: boolean): Promise { 8 | 9 | let chainId = (await ethers.provider.getNetwork()).chainId; 10 | 11 | const chainConfig = config[chainId]; 12 | 13 | const ClusterSelector = await ethers.getContractFactory("ClusterSelector"); 14 | 15 | var addresses: { [key: string]: { [key: string]: string } } = {}; 16 | if(!noLog) { 17 | console.log("Chain Id:", chainId); 18 | 19 | if (fs.existsSync("address.json")) { 20 | addresses = JSON.parse(fs.readFileSync("address.json", "utf8")); 21 | } 22 | 23 | if (addresses[chainId] === undefined) { 24 | addresses[chainId] = {}; 25 | } 26 | 27 | if (addresses[chainId]["ClusterSelector_"+network] !== undefined) { 28 | console.log("Existing deployment:", addresses[chainId]["ClusterSelector_"+network]); 29 | return ClusterSelector.attach(addresses[chainId]["ClusterSelector_"+network]); 30 | } 31 | } 32 | 33 | if(admin == undefined) { 34 | admin = chainConfig.admin; 35 | } 36 | 37 | if(startTime == undefined) startTime = chainConfig.startTime; 38 | if(epochLength == undefined) epochLength = chainConfig.epochLength; 39 | if(arbGasInfo == undefined) arbGasInfo = chainConfig.arbGasInfo; 40 | if(maxReward == undefined) maxReward = chainConfig.clusterSelection.maxReward; 41 | if(gasRefund === undefined) gasRefund = chainConfig.clusterSelection.gasRefund; 42 | 43 | const clusterSelector = await upgrades.deployProxy(ClusterSelector, [ 44 | admin, 45 | rewardDelegators, 46 | ], { 47 | kind: "uups", 48 | constructorArgs: [startTime, epochLength, arbGasInfo, maxReward, gasRefund] 49 | }); 50 | 51 | if(!noLog) { 52 | console.log("Deployed addr:", clusterSelector.address); 53 | 54 | addresses[chainId]["ClusterSelector_"+network] = clusterSelector.address; 55 | 56 | fs.writeFileSync("address.json", JSON.stringify(addresses, null, 2), "utf8"); 57 | } 58 | 59 | return clusterSelector; 60 | } 61 | 62 | export async function upgrade(network: string, startTime?: string, epochLength?: number, arbGasInfo?: string, maxReward?: string, gasRefund?: number) { 63 | let chainId = (await ethers.provider.getNetwork()).chainId; 64 | const chainConfig = config[chainId]; 65 | 66 | if(startTime == undefined) startTime = chainConfig.startTime; 67 | if(epochLength == undefined) epochLength = chainConfig.epochLength; 68 | if(arbGasInfo == undefined) arbGasInfo = chainConfig.arbGasInfo; 69 | if(maxReward == undefined) maxReward = chainConfig.clusterSelection.maxReward; 70 | if(gasRefund === undefined) gasRefund = chainConfig.clusterSelection.gasRefund; 71 | 72 | await upgradeUtil("ClusterSelector", `ClusterSelector_${network}`, [ 73 | startTime, epochLength, arbGasInfo, maxReward, gasRefund 74 | ]); 75 | } 76 | 77 | export async function verify(network: string) { 78 | let chainId = (await ethers.provider.getNetwork()).chainId; 79 | console.log("Chain Id:", chainId); 80 | 81 | const chainConfig = config[chainId]; 82 | 83 | var addresses: {[key: string]: {[key: string]: string}} = {}; 84 | if(fs.existsSync('address.json')) { 85 | addresses = JSON.parse(fs.readFileSync('address.json', 'utf8')); 86 | } 87 | 88 | if(addresses[chainId] === undefined || addresses[chainId]["ClusterSelector_"+network] === undefined) { 89 | throw new Error("Cluster Selector not deployed"); 90 | } 91 | 92 | const implAddress = await upgrades.erc1967.getImplementationAddress(addresses[chainId]["ClusterSelector_"+network]); 93 | 94 | await run("verify:verify", { 95 | address: implAddress, 96 | constructorArguments: [ 97 | chainConfig.startTime, 98 | chainConfig.epochLength, 99 | chainConfig.arbGasInfo, 100 | chainConfig.clusterSelection.maxReward, 101 | chainConfig.clusterSelection.gasRefund 102 | ] 103 | }); 104 | 105 | console.log("Cluster Selector verified"); 106 | } -------------------------------------------------------------------------------- /deployments/staking/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "42161": { 3 | "startTime": 1677888000, 4 | "epochLength": 900, 5 | "staking": { 6 | "receiver": { 7 | "token": "0xdA0a57B710768ae17941a9Fa33f8B720c8bD9ddD" 8 | }, 9 | "rewardWeights": { 10 | "ETH": "0" 11 | } 12 | }, 13 | "admin": "0x3F5f1Dc1af8897eA74Bf93bfA17F450D88af9FB0", 14 | "selectionReward": { 15 | "token": "0xdA0a57B710768ae17941a9Fa33f8B720c8bD9ddD", 16 | "amount": "0" 17 | }, 18 | "clusterSelection": { 19 | "gasRefund": "300000", 20 | "maxReward": "285000000000000" 21 | }, 22 | "arbGasInfo": "0x000000000000000000000000000000000000006C" 23 | }, 24 | "421614": { 25 | "cluster": { 26 | "waitTimes": [ 27 | 180, 28 | 240, 29 | 300 30 | ] 31 | }, 32 | "startTime": 1705536000, 33 | "epochLength": 900, 34 | "noOfClustersToSelect": 5, 35 | "clusterSelection": { 36 | "gasRefund": "300000", 37 | "maxReward": "285000000000000" 38 | }, 39 | "staking": { 40 | "tokens": { 41 | "POND": { 42 | "id": "0x5802add45f8ec0a524470683e7295faacc853f97cf4a8d3ffbaaf25ce0fd87c4", 43 | "address": "0x8995673cef56d4cBAE3cE96157a8fc977D4f8525", 44 | "rewardFactor": "100", 45 | "weightForThreshold": "0", 46 | "weightForDelegation": "1", 47 | "delegatable": false 48 | }, 49 | "MPOND": { 50 | "id": "0x1635815984abab0dbb9afd77984dad69c24bf3d711bc0ddb1e2d53ef2d523e5e", 51 | "address": "0x071BaFF53B0Ca54E4aec59Cda64f9cC5a7205e2c", 52 | "rewardFactor": "100", 53 | "weightForThreshold": "1000000", 54 | "weightForDelegation": "1000000", 55 | "delegatable": true 56 | } 57 | }, 58 | "thresholdForSelection": { 59 | "ETH": "500000000000000000" 60 | }, 61 | "rewardWeights": { 62 | "ETH": "1000000000000000000" 63 | }, 64 | "waitTimes": { 65 | "redelegation": 360, 66 | "undelegation": 420 67 | }, 68 | "receiver": { 69 | "token": "0x8995673cef56d4cBAE3cE96157a8fc977D4f8525" 70 | } 71 | }, 72 | "arbGasInfo": "0x000000000000000000000000000000000000006C", 73 | "totalRewardsPerEpoch": "4692550000000000000000", 74 | "admin": "0xd7E109d2219b5b5b90656FB8B33F2ba679b22062" 75 | }, 76 | "31337": { 77 | "cluster": { 78 | "waitTimes": [ 79 | 180, 80 | 240, 81 | 300 82 | ] 83 | }, 84 | "noOfClustersToSelect": 5, 85 | "clusterSelection": { 86 | "gasRefund": "300000", 87 | "maxGasPrice": "1000000000", 88 | "maxBaseL1Gas": "500000" 89 | }, 90 | "staking": { 91 | "tokens": { 92 | "POND": { 93 | "id": "0x5802add45f8ec0a524470683e7295faacc853f97cf4a8d3ffbaaf25ce0fd87c4", 94 | "rewardFactor": "100", 95 | "weightForThreshold": "0", 96 | "weightForDelegation": "1", 97 | "delegatable": false 98 | }, 99 | "MPOND": { 100 | "id": "0x1635815984abab0dbb9afd77984dad69c24bf3d711bc0ddb1e2d53ef2d523e5e", 101 | "rewardFactor": "100", 102 | "weightForThreshold": "1000000", 103 | "weightForDelegation": "1000000", 104 | "delegatable": true 105 | } 106 | }, 107 | "thresholdForSelection": { 108 | "ETH": "500000000000000000" 109 | }, 110 | "rewardWeights": { 111 | "ETH": "1000000000000000000" 112 | }, 113 | "waitTimes": { 114 | "redelegation": 360, 115 | "undelegation": 420 116 | } 117 | }, 118 | "totalRewardsPerEpoch": "4692550000000000000000" 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /deployments/enclaves/AttestationVerifier.ts: -------------------------------------------------------------------------------- 1 | import { ethers, upgrades, run } from 'hardhat'; 2 | import { Contract } from 'ethers'; 3 | import * as fs from 'fs'; 4 | import { AttestationVerifier } from "../../typechain-types"; 5 | import { upgrade as upgradeUtil } from '../utils/Upgrade'; 6 | const config = require('./config'); 7 | 8 | export async function deploy(enclaveImages?: AttestationVerifier.EnclaveImageStruct[], enclaveKeys?: string[], noLog?: boolean): Promise { 9 | let chainId = (await ethers.provider.getNetwork()).chainId; 10 | 11 | const chainConfig = config[chainId]; 12 | 13 | const AttestationVerifier = await ethers.getContractFactory('AttestationVerifier'); 14 | 15 | var addresses: { [key: string]: { [key: string]: string } } = {}; 16 | if (!noLog) { 17 | console.log("Chain Id:", chainId); 18 | 19 | if (fs.existsSync('address.json')) { 20 | addresses = JSON.parse(fs.readFileSync('address.json', 'utf8')); 21 | } 22 | 23 | if (addresses[chainId] === undefined) { 24 | addresses[chainId] = {}; 25 | } 26 | 27 | if (addresses[chainId]['AttestationVerifier'] !== undefined) { 28 | console.log("Existing deployment:", addresses[chainId]['AttestationVerifier']); 29 | return AttestationVerifier.attach(addresses[chainId]['AttestationVerifier']); 30 | } 31 | } 32 | 33 | if (enclaveImages === undefined) { 34 | enclaveImages = []; 35 | for (let i = 0; i < chainConfig.enclaves.whitelistedImages.length; i++) { 36 | if (chainConfig.enclaves.whitelistedImages[i].PCR === undefined) { 37 | throw new Error(`Image ${i}: PCR not defined for image`); 38 | } 39 | if (chainConfig.enclaves.whitelistedImages[i].PCR.PCR0.length !== 96) { 40 | throw new Error(`Image ${i}: PCR0 length is not 96`); 41 | } 42 | if (chainConfig.enclaves.whitelistedImages[i].PCR.PCR1.length !== 96) { 43 | throw new Error(`Image ${i}: PCR1 length is not 96`); 44 | } 45 | if (chainConfig.enclaves.whitelistedImages[i].PCR.PCR2.length !== 96) { 46 | throw new Error(`Image ${i}: PCR2 length is not 96`); 47 | } 48 | const image = { 49 | PCR0: "0x" + chainConfig.enclaves.whitelistedImages[i].PCR.PCR0, 50 | PCR1: "0x" + chainConfig.enclaves.whitelistedImages[i].PCR.PCR1, 51 | PCR2: "0x" + chainConfig.enclaves.whitelistedImages[i].PCR.PCR2, 52 | }; 53 | enclaveImages.push(image); 54 | } 55 | } 56 | 57 | if (enclaveKeys === undefined) { 58 | enclaveKeys = []; 59 | for (let i = 0; i < chainConfig.enclaves.whitelistedImages.length; i++) { 60 | if (chainConfig.enclaves.whitelistedImages[i].enclaveKey === undefined) { 61 | throw new Error(`Image ${i}: Enclave key not defined for image`); 62 | } 63 | enclaveKeys.push(chainConfig.enclaves.whitelistedImages[i].enclaveKey); 64 | } 65 | } 66 | 67 | let admin = chainConfig.admin; 68 | 69 | let attestationVerifier = await upgrades.deployProxy(AttestationVerifier, [enclaveImages, enclaveKeys, admin], { kind: "uups" }); 70 | 71 | if (!noLog) { 72 | console.log("Deployed addr:", attestationVerifier.address); 73 | 74 | addresses[chainId]['AttestationVerifier'] = attestationVerifier.address; 75 | 76 | fs.writeFileSync('address.json', JSON.stringify(addresses, null, 2), 'utf8'); 77 | } 78 | 79 | return attestationVerifier; 80 | } 81 | 82 | export async function upgrade() { 83 | await upgradeUtil('AttestationVerifier', 'AttestationVerifier', []); 84 | } 85 | 86 | export async function verify() { 87 | let chainId = (await ethers.provider.getNetwork()).chainId; 88 | console.log("Chain Id:", chainId); 89 | 90 | var addresses: { [key: string]: { [key: string]: string } } = {}; 91 | if (fs.existsSync('address.json')) { 92 | addresses = JSON.parse(fs.readFileSync('address.json', 'utf8')); 93 | } 94 | 95 | if (addresses[chainId] === undefined || addresses[chainId]['AttestationVerifier'] === undefined) { 96 | throw new Error("Attestation Verifier not deployed"); 97 | } 98 | 99 | const implAddress = await upgrades.erc1967.getImplementationAddress(addresses[chainId]['AttestationVerifier']); 100 | 101 | await run("verify:verify", { 102 | address: implAddress, 103 | constructorArguments: [] 104 | }); 105 | 106 | console.log("Attestation Verifier verified"); 107 | } 108 | -------------------------------------------------------------------------------- /contracts/token/Pond.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; 6 | import "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol"; 7 | import "@openzeppelin/contracts-upgradeable/utils/introspection/ERC165Upgradeable.sol"; 8 | import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol"; 9 | import "@openzeppelin/contracts-upgradeable/access/AccessControlEnumerableUpgradeable.sol"; 10 | import "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol"; 11 | import "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20CappedUpgradeable.sol"; 12 | import "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20BurnableUpgradeable.sol"; 13 | import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; 14 | 15 | import "./IArbToken.sol"; 16 | 17 | contract Pond is 18 | Initializable, // initializer 19 | ContextUpgradeable, // _msgSender, _msgData 20 | ERC165Upgradeable, // supportsInterface 21 | AccessControlUpgradeable, // RBAC 22 | AccessControlEnumerableUpgradeable, // RBAC enumeration 23 | ERC20Upgradeable, // token 24 | ERC20CappedUpgradeable, // supply cap 25 | ERC1967UpgradeUpgradeable, // delegate slots, proxy admin, private upgrade 26 | UUPSUpgradeable, // public upgrade 27 | IArbToken // Arbitrum bridge support 28 | { 29 | // in case we add more contracts in the inheritance chain 30 | uint256[500] private __gap0; 31 | 32 | /// @custom:oz-upgrades-unsafe-allow constructor 33 | // initializes the logic contract without any admins 34 | // safeguard against takeover of the logic contract 35 | constructor() initializer {} 36 | 37 | function initialize( 38 | string memory _name, 39 | string memory _symbol 40 | ) public initializer { 41 | __Context_init_unchained(); 42 | __ERC165_init_unchained(); 43 | __AccessControl_init_unchained(); 44 | __AccessControlEnumerable_init_unchained(); 45 | __ERC20_init_unchained(_name, _symbol); 46 | __ERC20Capped_init_unchained(10000000000e18); 47 | __ERC1967Upgrade_init_unchained(); 48 | __UUPSUpgradeable_init_unchained(); 49 | 50 | _setupRole(DEFAULT_ADMIN_ROLE, _msgSender()); 51 | _setRoleAdmin(BRIDGE_ROLE, DEFAULT_ADMIN_ROLE); 52 | _mint(_msgSender(), 10000000000e18); 53 | } 54 | 55 | function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165Upgradeable, AccessControlUpgradeable, AccessControlEnumerableUpgradeable) returns (bool) { 56 | return interfaceId == type(IArbToken).interfaceId || super.supportsInterface(interfaceId); 57 | } 58 | 59 | function _grantRole(bytes32 role, address account) internal virtual override(AccessControlUpgradeable, AccessControlEnumerableUpgradeable) { 60 | super._grantRole(role, account); 61 | } 62 | 63 | function _revokeRole(bytes32 role, address account) internal virtual override(AccessControlUpgradeable, AccessControlEnumerableUpgradeable) { 64 | super._revokeRole(role, account); 65 | 66 | // protect against accidentally removing all admins 67 | require(getRoleMemberCount(DEFAULT_ADMIN_ROLE) != 0, "Cannot be adminless"); 68 | } 69 | 70 | function _mint(address account, uint256 amount) internal virtual override(ERC20Upgradeable, ERC20CappedUpgradeable) { 71 | super._mint(account, amount); 72 | } 73 | 74 | function _authorizeUpgrade(address /*account*/) internal view override { 75 | require(hasRole(DEFAULT_ADMIN_ROLE, _msgSender()), "Pond: must be admin to upgrade"); 76 | } 77 | 78 | //-------------------------------- Bridge start --------------------------------// 79 | 80 | // bridge mint/burn functions are implemented using transfers to/from the token contract itself 81 | // limits exposure to contract balance in case the bridge is compromised 82 | 83 | bytes32 public constant BRIDGE_ROLE = keccak256("BRIDGE_ROLE"); 84 | 85 | address public l1Address; 86 | uint256[49] private __gap1; 87 | 88 | modifier onlyAdmin() { 89 | require(hasRole(DEFAULT_ADMIN_ROLE, _msgSender())); 90 | _; 91 | } 92 | 93 | modifier onlyBridge() { 94 | require(hasRole(BRIDGE_ROLE, _msgSender())); 95 | _; 96 | } 97 | 98 | function setL1Address(address _l1Address) external onlyAdmin { 99 | l1Address = _l1Address; 100 | } 101 | 102 | function bridgeMint(address _account, uint256 _amount) external onlyBridge { 103 | _transfer(address(this), _account, _amount); 104 | } 105 | 106 | function bridgeBurn(address _account, uint256 _amount) external onlyBridge { 107 | _transfer(_account, address(this), _amount); 108 | } 109 | 110 | function withdraw(uint256 _amount) external onlyAdmin { 111 | _transfer(address(this), _msgSender(), _amount); 112 | } 113 | 114 | //-------------------------------- Bridge end --------------------------------// 115 | } 116 | 117 | -------------------------------------------------------------------------------- /poetry.lock: -------------------------------------------------------------------------------- 1 | [[package]] 2 | name = "crytic-compile" 3 | version = "0.2.1" 4 | description = "Util to facilitate smart contracts compilation." 5 | category = "dev" 6 | optional = false 7 | python-versions = ">=3.6" 8 | 9 | [package.dependencies] 10 | pysha3 = ">=1.0.2" 11 | 12 | [[package]] 13 | name = "prettytable" 14 | version = "2.4.0" 15 | description = "A simple Python library for easily displaying tabular data in a visually appealing ASCII table format" 16 | category = "dev" 17 | optional = false 18 | python-versions = ">=3.6" 19 | 20 | [package.dependencies] 21 | wcwidth = "*" 22 | 23 | [package.extras] 24 | tests = ["pytest", "pytest-cov", "pytest-lazy-fixture"] 25 | 26 | [[package]] 27 | name = "pysha3" 28 | version = "1.0.2" 29 | description = "SHA-3 (Keccak) for Python 2.7 - 3.5" 30 | category = "dev" 31 | optional = false 32 | python-versions = "*" 33 | 34 | [[package]] 35 | name = "slither-analyzer" 36 | version = "0.8.1" 37 | description = "Slither is a Solidity static analysis framework written in Python 3." 38 | category = "dev" 39 | optional = false 40 | python-versions = ">=3.6" 41 | 42 | [package.dependencies] 43 | crytic-compile = ">=0.2.1" 44 | prettytable = ">=0.7.2" 45 | pysha3 = ">=1.0.2" 46 | 47 | [[package]] 48 | name = "wcwidth" 49 | version = "0.2.5" 50 | description = "Measures the displayed width of unicode strings in a terminal" 51 | category = "dev" 52 | optional = false 53 | python-versions = "*" 54 | 55 | [metadata] 56 | lock-version = "1.1" 57 | python-versions = "^3.9" 58 | content-hash = "e9022914b16e80397faa496a7bac2efe7f36180358d45a52390a4dade2fccf5a" 59 | 60 | [metadata.files] 61 | crytic-compile = [ 62 | {file = "crytic-compile-0.2.1.tar.gz", hash = "sha256:33794f848a62cb6dd72d691f5bed828353c986da3ba77bd8bceb6b4be9b6ac2f"}, 63 | ] 64 | prettytable = [ 65 | {file = "prettytable-2.4.0-py3-none-any.whl", hash = "sha256:2492f29e8686bdbcce815a568bff74cb71cbb704747c3abb9c9c6cfe25f985a2"}, 66 | {file = "prettytable-2.4.0.tar.gz", hash = "sha256:18e56447f636b447096977d468849c1e2d3cfa0af8e7b5acfcf83a64790c0aca"}, 67 | ] 68 | pysha3 = [ 69 | {file = "pysha3-1.0.2-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:6e6a84efb7856f5d760ee55cd2b446972cb7b835676065f6c4f694913ea8f8d9"}, 70 | {file = "pysha3-1.0.2-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:f9046d59b3e72aa84f6dae83a040bd1184ebd7fef4e822d38186a8158c89e3cf"}, 71 | {file = "pysha3-1.0.2-cp27-cp27m-win32.whl", hash = "sha256:9fdd28884c5d0b4edfed269b12badfa07f1c89dbc5c9c66dd279833894a9896b"}, 72 | {file = "pysha3-1.0.2-cp27-cp27m-win_amd64.whl", hash = "sha256:41be70b06c8775a9e4d4eeb52f2f6a3f356f17539a54eac61f43a29e42fd453d"}, 73 | {file = "pysha3-1.0.2-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:68c3a60a39f9179b263d29e221c1bd6e01353178b14323c39cc70593c30f21c5"}, 74 | {file = "pysha3-1.0.2-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:59111c08b8f34495575d12e5f2ce3bafb98bea470bc81e70c8b6df99aef0dd2f"}, 75 | {file = "pysha3-1.0.2-cp33-cp33m-win32.whl", hash = "sha256:571a246308a7b63f15f5aa9651f99cf30f2a6acba18eddf28f1510935968b603"}, 76 | {file = "pysha3-1.0.2-cp33-cp33m-win_amd64.whl", hash = "sha256:93abd775dac570cb9951c4e423bcb2bc6303a9d1dc0dc2b7afa2dd401d195b24"}, 77 | {file = "pysha3-1.0.2-cp34-cp34m-manylinux1_i686.whl", hash = "sha256:11a2ba7a2e1d9669d0052fc8fb30f5661caed5512586ecbeeaf6bf9478ab5c48"}, 78 | {file = "pysha3-1.0.2-cp34-cp34m-manylinux1_x86_64.whl", hash = "sha256:5ec8da7c5c70a53b5fa99094af3ba8d343955b212bc346a0d25f6ff75853999f"}, 79 | {file = "pysha3-1.0.2-cp34-cp34m-win32.whl", hash = "sha256:9c778fa8b161dc9348dc5cc361e94d54aa5ff18413788f4641f6600d4893a608"}, 80 | {file = "pysha3-1.0.2-cp34-cp34m-win_amd64.whl", hash = "sha256:fd7e66999060d079e9c0e8893e78d8017dad4f59721f6fe0be6307cd32127a07"}, 81 | {file = "pysha3-1.0.2-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:827b308dc025efe9b6b7bae36c2e09ed0118a81f792d888548188e97b9bf9a3d"}, 82 | {file = "pysha3-1.0.2-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:4416f16b0f1605c25f627966f76873e432971824778b369bd9ce1bb63d6566d9"}, 83 | {file = "pysha3-1.0.2-cp35-cp35m-win32.whl", hash = "sha256:c93a2676e6588abcfaecb73eb14485c81c63b94fca2000a811a7b4fb5937b8e8"}, 84 | {file = "pysha3-1.0.2-cp35-cp35m-win_amd64.whl", hash = "sha256:684cb01d87ed6ff466c135f1c83e7e4042d0fc668fa20619f581e6add1d38d77"}, 85 | {file = "pysha3-1.0.2-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:386998ee83e313b6911327174e088021f9f2061cbfa1651b97629b761e9ef5c4"}, 86 | {file = "pysha3-1.0.2-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:c7c2adcc43836223680ebdf91f1d3373543dc32747c182c8ca2e02d1b69ce030"}, 87 | {file = "pysha3-1.0.2-cp36-cp36m-win32.whl", hash = "sha256:cd5c961b603bd2e6c2b5ef9976f3238a561c58569945d4165efb9b9383b050ef"}, 88 | {file = "pysha3-1.0.2-cp36-cp36m-win_amd64.whl", hash = "sha256:0060a66be16665d90c432f55a0ba1f6480590cfb7d2ad389e688a399183474f0"}, 89 | {file = "pysha3-1.0.2.tar.gz", hash = "sha256:fe988e73f2ce6d947220624f04d467faf05f1bbdbc64b0a201296bb3af92739e"}, 90 | ] 91 | slither-analyzer = [ 92 | {file = "slither-analyzer-0.8.1.tar.gz", hash = "sha256:e49805e778a9ef66e77bc02519fd76e85208bd78bebbbae8bc998c4827198c44"}, 93 | ] 94 | wcwidth = [ 95 | {file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"}, 96 | {file = "wcwidth-0.2.5.tar.gz", hash = "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"}, 97 | ] 98 | -------------------------------------------------------------------------------- /contracts/enclaves/AttestationAutherSample.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import "@openzeppelin/contracts-upgradeable/utils/cryptography/ECDSAUpgradeable.sol"; 6 | import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; 7 | import "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol"; 8 | import "@openzeppelin/contracts-upgradeable/utils/introspection/ERC165Upgradeable.sol"; 9 | import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol"; 10 | import "@openzeppelin/contracts-upgradeable/access/AccessControlEnumerableUpgradeable.sol"; 11 | import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; 12 | import "./AttestationAutherUpgradeable.sol"; 13 | 14 | contract AttestationAutherSample is Initializable, // initializer 15 | ContextUpgradeable, // _msgSender, _msgData 16 | ERC165Upgradeable, // supportsInterface 17 | AccessControlUpgradeable, // RBAC 18 | AccessControlEnumerableUpgradeable, // RBAC enumeration 19 | ERC1967UpgradeUpgradeable, // delegate slots, proxy admin, private upgrade 20 | UUPSUpgradeable, // public upgrade 21 | AttestationAutherUpgradeable // auther 22 | { 23 | // in case we add more contracts in the inheritance chain 24 | uint256[500] private __gap_0; 25 | 26 | /// @custom:oz-upgrades-unsafe-allow constructor 27 | // initializes the logic contract without any admins 28 | // safeguard against takeover of the logic contract 29 | constructor(IAttestationVerifier attestationVerifier, uint256 maxAge) 30 | initializer 31 | AttestationAutherUpgradeable(attestationVerifier, maxAge) {} 32 | 33 | modifier onlyAdmin() { 34 | require(hasRole(DEFAULT_ADMIN_ROLE, _msgSender()), "only admin"); 35 | _; 36 | } 37 | 38 | //-------------------------------- Overrides start --------------------------------// 39 | 40 | function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165Upgradeable, AccessControlUpgradeable, AccessControlEnumerableUpgradeable) returns (bool) { 41 | return super.supportsInterface(interfaceId); 42 | } 43 | 44 | function _grantRole(bytes32 role, address account) internal virtual override(AccessControlUpgradeable, AccessControlEnumerableUpgradeable) { 45 | super._grantRole(role, account); 46 | } 47 | 48 | function _revokeRole(bytes32 role, address account) internal virtual override(AccessControlUpgradeable, AccessControlEnumerableUpgradeable) { 49 | super._revokeRole(role, account); 50 | 51 | // protect against accidentally removing all admins 52 | require(getRoleMemberCount(DEFAULT_ADMIN_ROLE) != 0, "AAS:RR-All admins cant be removed"); 53 | } 54 | 55 | function _authorizeUpgrade(address /*account*/) onlyAdmin internal view override {} 56 | 57 | //-------------------------------- Overrides end --------------------------------// 58 | 59 | //-------------------------------- Initializer start --------------------------------// 60 | 61 | function initialize(EnclaveImage[] memory images, address _admin) external initializer { 62 | require(images.length != 0, "AAS:I-At least one image necessary"); 63 | require(_admin != address(0), "AAS:I-At least one admin necessary"); 64 | 65 | __Context_init_unchained(); 66 | __ERC165_init_unchained(); 67 | __AccessControl_init_unchained(); 68 | __AccessControlEnumerable_init_unchained(); 69 | __ERC1967Upgrade_init_unchained(); 70 | __UUPSUpgradeable_init_unchained(); 71 | __AttestationAuther_init_unchained(images); 72 | 73 | _setupRole(DEFAULT_ADMIN_ROLE, _admin); 74 | } 75 | 76 | //-------------------------------- Initializer start --------------------------------// 77 | 78 | //-------------------------------- Admin methods start --------------------------------// 79 | 80 | function whitelistEnclaveImage(bytes memory PCR0, bytes memory PCR1, bytes memory PCR2) external onlyAdmin returns (bytes32) { 81 | return _whitelistEnclaveImage(EnclaveImage(PCR0, PCR1, PCR2)); 82 | } 83 | 84 | function revokeEnclaveImage(bytes32 imageId) external onlyAdmin { 85 | return _revokeEnclaveImage(imageId); 86 | } 87 | 88 | function whitelistEnclaveKey(bytes memory enclavePubKey, bytes32 imageId) external onlyAdmin { 89 | return _whitelistEnclaveKey(enclavePubKey, imageId); 90 | } 91 | 92 | function revokeEnclaveKey(bytes memory enclavePubKey) external onlyAdmin { 93 | return _revokeEnclaveKey(enclavePubKey); 94 | } 95 | 96 | //-------------------------------- Admin methods end --------------------------------// 97 | 98 | //-------------------------------- Open methods start -------------------------------// 99 | 100 | string public constant SIGNATURE_PREFIX = "attestation-auther-sample-"; 101 | 102 | function verify( 103 | bytes memory signature, 104 | string memory message 105 | ) external view { 106 | bytes32 digest = keccak256(abi.encodePacked( 107 | SIGNATURE_PREFIX, 108 | message 109 | )); 110 | 111 | address signer = ECDSAUpgradeable.recover(digest, signature); 112 | 113 | _allowOnlyVerified(signer); 114 | } 115 | 116 | //-------------------------------- Open methods end -------------------------------// 117 | } 118 | -------------------------------------------------------------------------------- /test/helpers/rbac.ts: -------------------------------------------------------------------------------- 1 | import { expect } from "chai"; 2 | import { Contract, Signer } from "ethers"; 3 | import { ethers, network } from "hardhat"; 4 | 5 | export function testAdminRole(name: string, deployer: (signers: Signer[], addrs: string[]) => Promise) { 6 | describe(name, function () { 7 | let signers: Signer[]; 8 | let addrs: string[]; 9 | let contract: Contract; 10 | let DEFAULT_ADMIN_ROLE: string; 11 | 12 | let snapshot: any; 13 | 14 | before(async function () { 15 | signers = await ethers.getSigners(); 16 | addrs = await Promise.all(signers.map((a) => a.getAddress())); 17 | contract = await deployer(signers, addrs); 18 | DEFAULT_ADMIN_ROLE = await contract.DEFAULT_ADMIN_ROLE(); 19 | }); 20 | 21 | beforeEach(async function () { 22 | snapshot = await network.provider.request({ 23 | method: "evm_snapshot", 24 | params: [], 25 | }); 26 | }); 27 | 28 | afterEach(async function () { 29 | await network.provider.request({ 30 | method: "evm_revert", 31 | params: [snapshot], 32 | }); 33 | }); 34 | 35 | it("admin can grant admin role", async function () { 36 | await contract.grantRole(DEFAULT_ADMIN_ROLE, addrs[1]); 37 | expect(await contract.hasRole(DEFAULT_ADMIN_ROLE, addrs[1])).to.be.true; 38 | }); 39 | 40 | it("non admin cannot grant admin role", async function () { 41 | await expect(contract.connect(signers[1]).grantRole(DEFAULT_ADMIN_ROLE, addrs[1])).to.be.reverted; 42 | }); 43 | 44 | it("admin can revoke admin role", async function () { 45 | await contract.grantRole(DEFAULT_ADMIN_ROLE, addrs[1]); 46 | expect(await contract.hasRole(DEFAULT_ADMIN_ROLE, addrs[1])).to.be.true; 47 | 48 | await contract.revokeRole(DEFAULT_ADMIN_ROLE, addrs[1]); 49 | expect(await contract.hasRole(DEFAULT_ADMIN_ROLE, addrs[1])).to.be.false; 50 | }); 51 | 52 | it("non admin cannot revoke admin role", async function () { 53 | await contract.grantRole(DEFAULT_ADMIN_ROLE, addrs[1]); 54 | expect(await contract.hasRole(DEFAULT_ADMIN_ROLE, addrs[1])).to.be.true; 55 | 56 | await expect(contract.connect(signers[2]).revokeRole(DEFAULT_ADMIN_ROLE, addrs[1])).to.be.reverted; 57 | }); 58 | 59 | it("admin can renounce own admin role if there are other admins", async function () { 60 | await contract.grantRole(DEFAULT_ADMIN_ROLE, addrs[1]); 61 | expect(await contract.hasRole(DEFAULT_ADMIN_ROLE, addrs[1])).to.be.true; 62 | 63 | await contract.connect(signers[1]).renounceRole(DEFAULT_ADMIN_ROLE, addrs[1]); 64 | expect(await contract.hasRole(DEFAULT_ADMIN_ROLE, addrs[1])).to.be.false; 65 | }); 66 | 67 | it("admin cannot renounce own admin role if there are no other admins", async function () { 68 | await expect(contract.renounceRole(DEFAULT_ADMIN_ROLE, addrs[0])).to.be.reverted; 69 | }); 70 | 71 | it("admin cannot renounce admin role of other admins", async function () { 72 | await contract.grantRole(DEFAULT_ADMIN_ROLE, addrs[1]); 73 | expect(await contract.hasRole(DEFAULT_ADMIN_ROLE, addrs[1])).to.be.true; 74 | 75 | await expect(contract.renounceRole(DEFAULT_ADMIN_ROLE, addrs[1])).to.be.reverted; 76 | }); 77 | }); 78 | } 79 | 80 | export function testRole(name: string, deployer: (signers: Signer[], addrs: string[]) => Promise, role: string) { 81 | describe(name, function () { 82 | let signers: Signer[]; 83 | let addrs: string[]; 84 | let contract: Contract; 85 | let ROLE: string; 86 | 87 | let snapshot: any; 88 | 89 | before(async function () { 90 | signers = await ethers.getSigners(); 91 | addrs = await Promise.all(signers.map((a) => a.getAddress())); 92 | contract = await deployer(signers, addrs); 93 | ROLE = await contract[role](); 94 | }); 95 | 96 | beforeEach(async function () { 97 | snapshot = await network.provider.request({ 98 | method: "evm_snapshot", 99 | params: [], 100 | }); 101 | }); 102 | 103 | afterEach(async function () { 104 | await network.provider.request({ 105 | method: "evm_revert", 106 | params: [snapshot], 107 | }); 108 | }); 109 | 110 | it(`admin can grant ${role} role`, async function () { 111 | await contract.grantRole(ROLE, addrs[1]); 112 | expect(await contract.hasRole(ROLE, addrs[1])).to.be.true; 113 | }); 114 | 115 | it(`non admin cannot grant ${role} role`, async function () { 116 | await expect(contract.connect(signers[1]).grantRole(ROLE, addrs[1])).to.be.reverted; 117 | }); 118 | 119 | it(`admin can revoke ${role} role`, async function () { 120 | await contract.grantRole(ROLE, addrs[1]); 121 | expect(await contract.hasRole(ROLE, addrs[1])).to.be.true; 122 | 123 | await contract.revokeRole(ROLE, addrs[1]); 124 | expect(await contract.hasRole(ROLE, addrs[1])).to.be.false; 125 | }); 126 | 127 | it(`non admin cannot revoke ${role} role`, async function () { 128 | await contract.grantRole(ROLE, addrs[1]); 129 | expect(await contract.hasRole(ROLE, addrs[1])).to.be.true; 130 | 131 | await expect(contract.connect(signers[2]).revokeRole(ROLE, addrs[1])).to.be.reverted; 132 | }); 133 | 134 | it(`${role} signer can renounce own ${role} role`, async function () { 135 | await contract.grantRole(ROLE, addrs[1]); 136 | expect(await contract.hasRole(ROLE, addrs[1])).to.be.true; 137 | 138 | await contract.connect(signers[1]).renounceRole(ROLE, addrs[1]); 139 | expect(await contract.hasRole(ROLE, addrs[1])).to.be.false; 140 | }); 141 | }); 142 | } 143 | -------------------------------------------------------------------------------- /benchmarks/fixtures/ClusterRewards.ts: -------------------------------------------------------------------------------- 1 | import { ethers, upgrades } from "hardhat"; 2 | import { BigNumber, BigNumberish, Signer, utils, Wallet } from "ethers"; 3 | import { deploy as deployClusterRewards } from "../../deployments/staking/ClusterRewards"; 4 | import { deploy as deployClusterSelector } from "../../deployments/staking/ClusterSelector"; 5 | import { deploy as deployReceiverStaking } from "../../deployments/staking/ReceiverStaking"; 6 | import { ArbGasInfo__factory, ClusterRewards__factory, ClusterSelector__factory, Pond__factory, ReceiverStaking__factory } from "../../typechain-types"; 7 | 8 | // import { ArbGasInfo__factory } from "../../typechain-types"; 9 | const arbGasContract = '0x000000000000000000000000000000000000006c' 10 | const maxGasRefundOnClusterSelection = "10000000" 11 | const maxRewardOnClusterSelection = ethers.utils.parseEther("1").toString() 12 | const EPOCH_LENGTH = 15*60; 13 | 14 | const estimator = new ethers.Contract(arbGasContract, [ 15 | "function getPricesInArbGas() view returns(uint256 gasPerL2Tx, uint256 gasPerL1CallDataByte, uint256)" 16 | ]); 17 | const mainnetProvider = new ethers.providers.JsonRpcProvider("https://arb1.arbitrum.io/rpc"); 18 | 19 | export async function deployFixture() { 20 | const signers = await ethers.getSigners(); 21 | const addrs = await Promise.all(signers.map((a) => a.getAddress())); 22 | 23 | const blockNum = await ethers.provider.getBlockNumber(); 24 | const blockData = await ethers.provider.getBlock(blockNum); 25 | 26 | const Pond = await ethers.getContractFactory("Pond"); 27 | const pondInstance = await upgrades.deployProxy(Pond, ["Marlin POND", "POND"], { 28 | kind: "uups", 29 | }); 30 | const pond = Pond__factory.connect(pondInstance.address, signers[0]) 31 | 32 | const receiverStakingInstance = await deployReceiverStaking(addrs[0], blockData.timestamp, EPOCH_LENGTH, pond.address, true); 33 | const receiverStaking = ReceiverStaking__factory.connect(receiverStakingInstance.address, signers[0]) 34 | 35 | const arbGasInfo = await new ArbGasInfo__factory(signers[0]).deploy() 36 | const gasResult = await estimator.connect(mainnetProvider).getPricesInArbGas() 37 | await arbGasInfo.setPrices(gasResult[0], gasResult[1], gasResult[2]) 38 | 39 | const clusterSelectorInstance = await deployClusterSelector("ETH", 40 | addrs[1], 41 | arbGasInfo.address, 42 | addrs[0], 43 | blockData.timestamp, 44 | EPOCH_LENGTH, 45 | maxGasRefundOnClusterSelection, 46 | maxRewardOnClusterSelection, 47 | true); 48 | const clusterSelector = ClusterSelector__factory.connect(clusterSelectorInstance.address, signers[0]) 49 | 50 | const clusterRewardsInstance = await deployClusterRewards(addrs[1], receiverStaking.address, { 51 | "ETH": clusterSelector.address 52 | }, addrs[0], true); 53 | const clusterRewards = ClusterRewards__factory.connect(clusterRewardsInstance.address, signers[0]) 54 | 55 | return { 56 | pond, 57 | receiverStaking, 58 | clusterSelector, 59 | clusterRewards, 60 | admin: signers[0], 61 | rewardDelegatorsMock: signers[1] 62 | }; 63 | } 64 | 65 | export async function initDataFixture() { 66 | const nodesToInsert: number = 75; 67 | const receiverCount: number = 100; 68 | 69 | const signers = await ethers.getSigners(); 70 | const preAllocEthSigner = signers[8]; 71 | 72 | const { 73 | pond, 74 | receiverStaking, 75 | clusterSelector, 76 | clusterRewards, 77 | admin, 78 | rewardDelegatorsMock 79 | } = await deployFixture(); 80 | 81 | const tokenSupply: BigNumber = await pond.totalSupply(); 82 | 83 | const clusters: string[] = []; 84 | const balances: BigNumberish[] = []; 85 | const receivers: Signer[] = []; 86 | const receiverSigners: Signer[] = []; 87 | 88 | // generate clusters and balance data 89 | for(let i=0; i < nodesToInsert; i++) { 90 | const address = Wallet.createRandom().address; 91 | clusters.push(address); 92 | balances.push(BigNumber.from(ethers.utils.randomBytes(32)).mod(tokenSupply.div(utils.parseEther(nodesToInsert+"")))); 93 | } 94 | 95 | // insert clusterData into selector 96 | for(let i=0; i < clusters.length; i+=50) { 97 | await clusterSelector.connect(rewardDelegatorsMock).upsertMultiple(clusters.slice(i, i+50), balances.slice(i, i+50)); 98 | } 99 | 100 | for(let i=0; i < receiverCount; i++) { 101 | // create receiver and stake 102 | const receiver = Wallet.createRandom().connect(ethers.provider); 103 | const receiverSigner = Wallet.createRandom().connect(ethers.provider); 104 | receivers.push(receiver); 105 | receiverSigners.push(receiverSigner); 106 | const depositAmount = BigNumber.from(ethers.utils.randomBytes(32)).mod(tokenSupply.div(receiverCount)); 107 | await preAllocEthSigner.sendTransaction({ 108 | to: receiver.address, 109 | value: utils.parseEther("0.5").toString() 110 | }); 111 | await preAllocEthSigner.sendTransaction({ 112 | to: receiverSigner.address, 113 | value: utils.parseEther("0.5").toString() 114 | }); 115 | await pond.transfer(receiver.address, depositAmount); 116 | await pond.connect(receiver).approve(receiverStaking.address, depositAmount); 117 | await receiverStaking.connect(receiver).depositFor(depositAmount, receiverSigner.address); 118 | } 119 | 120 | return { 121 | pond, 122 | receiverStaking, 123 | clusterSelector, 124 | clusterRewards, 125 | admin, 126 | rewardDelegatorsMock, 127 | nodesInserted: nodesToInsert, 128 | receivers, 129 | receiverSigners 130 | }; 131 | } 132 | -------------------------------------------------------------------------------- /contracts/token/Credit.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.8.26; 4 | 5 | /* Libraries */ 6 | import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; 7 | 8 | /* Contracts */ 9 | import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; 10 | import {ContextUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol"; 11 | import {ERC165Upgradeable} from "@openzeppelin/contracts-upgradeable/utils/introspection/ERC165Upgradeable.sol"; 12 | import {AccessControlEnumerableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/AccessControlEnumerableUpgradeable.sol"; 13 | import {ERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol"; 14 | import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; 15 | import {PausableUpgradeable} from "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol"; 16 | 17 | /* Interfaces */ 18 | import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 19 | 20 | 21 | contract Credit is 22 | ContextUpgradeable, // _msgSender, _msgData 23 | AccessControlEnumerableUpgradeable, // RBAC enumeration 24 | ERC20Upgradeable, // token 25 | UUPSUpgradeable, // public upgrade 26 | PausableUpgradeable // pause/unpause 27 | { 28 | using SafeERC20 for IERC20; 29 | 30 | uint256[500] private __gap0; 31 | 32 | error OnlyAdmin(); 33 | error OnlyTransferAllowedRole(); 34 | error NoAdminExists(); 35 | error OnlyOysterMarket(); 36 | error NotEnoughUSDC(); 37 | error OnlyToEmergencyWithdrawRole(); 38 | 39 | bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE"); // 0x9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a6 40 | bytes32 public constant BURNER_ROLE = keccak256("BURNER_ROLE"); // 0x3c11d16cbaffd01df69ce1c404f6340ee057498f5f00246190ea54220576a848 41 | bytes32 public constant TRANSFER_ALLOWED_ROLE = keccak256("TRANSFER_ALLOWED_ROLE"); // 0xed89ee80d998965e2804dad373576bf7ffc490ba5986d52deb7d526e93617101 42 | bytes32 public constant REDEEMER_ROLE = keccak256("REDEEMER_ROLE"); // 0x44ac9762eec3a11893fefb11d028bb3102560094137c3ed4518712475b2577cc 43 | bytes32 public constant EMERGENCY_WITHDRAW_ROLE = keccak256("EMERGENCY_WITHDRAW_ROLE"); // 0x66f144ecd65ad16d38ecdba8687842af4bc05fde66fe3d999569a3006349785f 44 | 45 | modifier onlyAdmin() { 46 | require(hasRole(DEFAULT_ADMIN_ROLE, _msgSender()), OnlyAdmin()); 47 | _; 48 | } 49 | 50 | //-------------------------------- Overrides start --------------------------------/ 51 | 52 | function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { 53 | return super.supportsInterface(interfaceId); 54 | } 55 | 56 | function decimals() public pure override returns (uint8) { 57 | return 6; 58 | } 59 | 60 | function _revokeRole(bytes32 role, address account) internal override { 61 | super._revokeRole(role, account); 62 | 63 | // protect against accidentally removing all admins 64 | require(getRoleMemberCount(DEFAULT_ADMIN_ROLE) != 0, NoAdminExists()); 65 | } 66 | 67 | function _beforeTokenTransfer(address from, address to, uint256 /* amount */) internal virtual override { 68 | require(hasRole(TRANSFER_ALLOWED_ROLE, from) || hasRole(TRANSFER_ALLOWED_ROLE, to), OnlyTransferAllowedRole()); 69 | } 70 | 71 | function _authorizeUpgrade(address /*account*/) internal view override { 72 | require(hasRole(DEFAULT_ADMIN_ROLE, _msgSender()), OnlyAdmin()); 73 | } 74 | 75 | //-------------------------------- Overrides end --------------------------------// 76 | 77 | /// @custom:oz-upgrades-unsafe-allow state-variable-immutable 78 | address immutable USDC; 79 | 80 | uint256[500] private __gap1; 81 | 82 | //-------------------------------- Initializer start --------------------------------/ 83 | 84 | /// @custom:oz-upgrades-unsafe-allow constructor 85 | constructor(address _usdc) { 86 | USDC = _usdc; 87 | } 88 | 89 | function initialize(address _admin) public initializer { 90 | __Context_init_unchained(); 91 | __ERC165_init_unchained(); 92 | __AccessControlEnumerable_init_unchained(); 93 | __ERC20_init_unchained("Oyster Credit", "CREDIT"); 94 | __UUPSUpgradeable_init_unchained(); 95 | 96 | _grantRole(DEFAULT_ADMIN_ROLE, _admin); 97 | } 98 | 99 | //-------------------------------- Initializer end --------------------------------/ 100 | 101 | //-------------------------------- Token Mint/Burn start --------------------------------/ 102 | 103 | function mint(address _to, uint256 _amount) external whenNotPaused onlyRole(MINTER_ROLE) { 104 | _mint(_to, _amount); 105 | } 106 | 107 | function burn(address _from, uint256 _amount) external whenNotPaused onlyRole(BURNER_ROLE) { 108 | _burn(_from, _amount); 109 | } 110 | 111 | //-------------------------------- Token Mint/Burn end --------------------------------// 112 | 113 | //-------------------------------- Oyster Market start --------------------------------// 114 | 115 | function redeemAndBurn(address _to, uint256 _amount) external whenNotPaused onlyRole(REDEEMER_ROLE) { 116 | IERC20(USDC).safeTransfer(_to, _amount); 117 | _burn(_msgSender(), _amount); 118 | } 119 | 120 | //-------------------------------- Oyster Market end --------------------------------// 121 | 122 | //-------------------------------- Emergency Withdraw start --------------------------------// 123 | 124 | function emergencyWithdraw(address _token, address _to, uint256 _amount) external onlyAdmin { 125 | require(hasRole(EMERGENCY_WITHDRAW_ROLE, _to), OnlyToEmergencyWithdrawRole()); 126 | IERC20(_token).safeTransfer(_to, _amount); 127 | } 128 | 129 | //-------------------------------- Emergency Withdraw end --------------------------------// 130 | } -------------------------------------------------------------------------------- /scripts/deploy/enclaves/UpgradeMarketV1.ts: -------------------------------------------------------------------------------- 1 | import { ethers, upgrades, network, run } from 'hardhat'; 2 | import { getImplementationAddress } from '@openzeppelin/upgrades-core'; 3 | import { BigNumber as BN, Signer, Contract } from 'ethers'; 4 | import { Credit__factory, MarketV1__factory, UUPSUpgradeable__factory } from '../../../typechain-types'; 5 | import { ContractType, getConfig, verifyContract } from '../../helper/helper'; 6 | import * as fs from 'fs'; 7 | 8 | // 2024-03-20 Arbitrum Sepolia Upgrade Before Merge 9 | // Upgrade Original MarketV1 contract 10 | // Upgrade Credit Contract 11 | async function main() { 12 | await deployAndUpgradeMarketV1(); // admin: 0xd7E109d2219b5b5b90656FB8B33F2ba679b22062 13 | await deployAndUpgradeCredit(); // admin: 0x7C046645E21B811780Cf420021E6701A9E66935C 14 | } 15 | 16 | async function deployAndUpgradeMarketV1() { 17 | const { addresses, path } = await getConfig(); 18 | const signers = await ethers.getSigners(); 19 | const admin = signers[0]; // 0xd7E109d2219b5b5b90656FB8B33F2ba679b22062 20 | const FIVE_MINUTES = 5 * 60; 21 | 22 | /*////////////////////////////////////////////////////////////// 23 | UPGRADE ORIGINAL MARKETV1 24 | //////////////////////////////////////////////////////////////*/ 25 | 26 | // Deploy New Marketv1 Implementation 27 | const marketV1Proxy = UUPSUpgradeable__factory.connect(addresses.proxy.marketV1, admin); 28 | const newMarketV1Impl = await new MarketV1__factory(admin).deploy(); 29 | await newMarketV1Impl.deployed(); 30 | 31 | // save new marketV1 implementation address 32 | addresses.implementation.marketV1 = newMarketV1Impl.address; 33 | fs.writeFileSync(path, JSON.stringify(addresses, null, 4), "utf-8"); 34 | 35 | // Upgrade to new implementation 36 | const marketV1UpgradeTx = await marketV1Proxy.connect(admin).upgradeTo(newMarketV1Impl.address); 37 | await marketV1UpgradeTx.wait(); 38 | 39 | // Reinitialize MarketV1 (noticePeriod, creditToken) 40 | const marketV1 = MarketV1__factory.connect(marketV1Proxy.address, admin); 41 | const reinitializeTx = await marketV1.connect(admin).reinitialize(FIVE_MINUTES, addresses.proxy.credit); 42 | await reinitializeTx.wait(); 43 | 44 | /*////////////////////////////////////////////////////////////// 45 | VERIFY CONTRACTS 46 | //////////////////////////////////////////////////////////////*/ 47 | await verifyContract("MarketV1", ContractType.Implementation); 48 | } 49 | 50 | async function deployAndUpgradeCredit() { 51 | const { addresses, path } = await getConfig(); 52 | const signers = await ethers.getSigners(); 53 | const admin = signers[0]; // 0x7C046645E21B811780Cf420021E6701A9E66935C 54 | 55 | /*////////////////////////////////////////////////////////////// 56 | UPGRADE CREDIT CONTRACT 57 | //////////////////////////////////////////////////////////////*/ 58 | 59 | // Deploy New Credit Implementation 60 | const credit = Credit__factory.connect(addresses.proxy.credit, admin); 61 | const creditProxy = UUPSUpgradeable__factory.connect(addresses.proxy.credit, admin); 62 | const newCreditImpl = await new Credit__factory(admin).deploy(addresses.proxy.usdc); 63 | await newCreditImpl.deployed(); 64 | 65 | // save new credit implementation address 66 | addresses.implementation.credit = newCreditImpl.address; 67 | fs.writeFileSync(path, JSON.stringify(addresses, null, 4), "utf-8"); 68 | 69 | // Upgrade to new implementation 70 | const creditUpgradeTx = await creditProxy.connect(admin).upgradeTo(newCreditImpl.address); 71 | await creditUpgradeTx.wait(); 72 | 73 | /*////////////////////////////////////////////////////////////// 74 | GRANT ROLES TO MARKETV1 75 | //////////////////////////////////////////////////////////////*/ 76 | 77 | // Grant `TRANSFER_ALLOWED_ROLE` to MarketV1 78 | await credit.connect(admin).grantRole(await credit.TRANSFER_ALLOWED_ROLE(), addresses.proxy.marketV1); 79 | // Grant `REDEEMER_ROLE` to MarketV1 80 | await credit.connect(admin).grantRole(await credit.REDEEMER_ROLE(), addresses.proxy.marketV1); 81 | 82 | await verifyContract("Credit", ContractType.Implementation, [addresses.proxy.usdc]); 83 | } 84 | 85 | // Freshly Deploy MarketV1 86 | async function deployMarketV1() { 87 | const { addresses, path } = await getConfig(); 88 | const signers = await ethers.getSigners(); 89 | const admin = signers[0]; 90 | 91 | // MarketV1 92 | const MarketV1Contract = await ethers.getContractFactory("MarketV1"); 93 | const marketV1Proxy = await upgrades.deployProxy(MarketV1Contract, [], { 94 | kind: "uups", 95 | initializer: false, 96 | unsafeAllow: ["missing-initializer-call"], 97 | }); 98 | await marketV1Proxy.deployed(); 99 | const marketV1 = MarketV1__factory.connect(marketV1Proxy.address, admin); 100 | addresses.proxy.marketV1 = marketV1.address; 101 | addresses.implementation.marketV1 = await upgrades.erc1967.getImplementationAddress(marketV1.address); 102 | fs.writeFileSync(path, JSON.stringify(addresses, null, 4), "utf-8"); 103 | console.log("MarketV1 deployed at:\t\t", marketV1.address); 104 | 105 | // TODO: set shutdown window 106 | // TODO: set Credit Contract 107 | 108 | await verifyContract("MarketV1", ContractType.Proxy); 109 | await verifyContract("MarketV1", ContractType.Implementation); 110 | } 111 | 112 | // Freshly Deploy Credit 113 | async function deployCredit() { 114 | const { addresses, path } = await getConfig(); 115 | 116 | const signers = await ethers.getSigners(); 117 | const admin = signers[0]; 118 | 119 | // Credit 120 | const CreditContract = await ethers.getContractFactory("Credit"); 121 | const creditProxy = await upgrades.deployProxy(CreditContract, [], { 122 | kind: "uups", 123 | initializer: false, 124 | unsafeAllow: ["missing-initializer-call"], 125 | constructorArgs: [addresses.proxy.marketV1, addresses.proxy.usdc], 126 | }); 127 | await creditProxy.deployed(); 128 | const credit = Credit__factory.connect(creditProxy.address, admin); 129 | console.log("Credit deployed at:\t\t", credit.address); 130 | 131 | // Set Admin 132 | await credit.connect(admin).initialize(admin.address); 133 | 134 | // set addresses 135 | addresses.proxy.credit = credit.address; 136 | addresses.implementation.credit = await upgrades.erc1967.getImplementationAddress(credit.address); 137 | 138 | fs.writeFileSync(path, JSON.stringify(addresses, null, 4), "utf-8"); 139 | 140 | await verifyContract("Credit", ContractType.Proxy); 141 | await verifyContract("Credit", ContractType.Implementation); 142 | } 143 | 144 | main() 145 | .then(() => process.exit(0)) 146 | .catch((error) => { 147 | console.error(error); 148 | process.exit(1); 149 | }); 150 | 151 | // verify() 152 | // .then(() => process.exit(0)) 153 | // .catch((error) => { 154 | // console.error(error); 155 | // process.exit(1); 156 | // }); 157 | -------------------------------------------------------------------------------- /contracts/enclaves/AttestationAutherUpgradeable.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import "@openzeppelin/contracts-upgradeable/utils/cryptography/ECDSAUpgradeable.sol"; 6 | import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; 7 | import "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol"; 8 | import "@openzeppelin/contracts-upgradeable/utils/introspection/ERC165Upgradeable.sol"; 9 | import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol"; 10 | import "@openzeppelin/contracts-upgradeable/access/AccessControlEnumerableUpgradeable.sol"; 11 | import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; 12 | import "./interfaces/IAttestationVerifier.sol"; 13 | 14 | contract AttestationAutherUpgradeable is 15 | Initializable // initializer 16 | { 17 | /// @custom:oz-upgrades-unsafe-allow state-variable-immutable 18 | IAttestationVerifier public immutable ATTESTATION_VERIFIER; 19 | 20 | /// @custom:oz-upgrades-unsafe-allow state-variable-immutable 21 | uint256 public immutable ATTESTATION_MAX_AGE; 22 | 23 | /// @custom:oz-upgrades-unsafe-allow constructor 24 | constructor(IAttestationVerifier attestationVerifier, uint256 maxAge) { 25 | ATTESTATION_VERIFIER = attestationVerifier; 26 | ATTESTATION_MAX_AGE = maxAge; 27 | } 28 | 29 | struct EnclaveImage { 30 | bytes PCR0; 31 | bytes PCR1; 32 | bytes PCR2; 33 | } 34 | 35 | mapping(bytes32 => EnclaveImage) private whitelistedImages; 36 | mapping(address => bytes32) private verifiedKeys; 37 | 38 | uint256[48] private __gap; 39 | 40 | event EnclaveImageWhitelisted(bytes32 indexed imageId, bytes PCR0, bytes PCR1, bytes PCR2); 41 | event EnclaveImageRevoked(bytes32 indexed imageId); 42 | event EnclaveKeyWhitelisted(bytes indexed enclavePubKey, bytes32 indexed imageId); 43 | event EnclaveKeyRevoked(bytes indexed enclavePubKey); 44 | event EnclaveKeyVerified(bytes indexed enclavePubKey, bytes32 indexed imageId); 45 | 46 | function __AttestationAuther_init_unchained(EnclaveImage[] memory images) internal onlyInitializing { 47 | for (uint256 i = 0; i < images.length; i++) { 48 | _whitelistEnclaveImage(images[i]); 49 | } 50 | } 51 | 52 | function _pubKeyToAddress(bytes memory pubKey) internal pure returns (address) { 53 | require(pubKey.length == 64, "Invalid public key length"); 54 | 55 | bytes32 hash = keccak256(pubKey); 56 | return address(uint160(uint256(hash))); 57 | } 58 | 59 | function _whitelistEnclaveImage(EnclaveImage memory image) internal returns(bytes32) { 60 | require( 61 | image.PCR0.length == 48 && 62 | image.PCR1.length == 48 && 63 | image.PCR2.length == 48, 64 | "AA:WI-PCR values must be 48 bytes" 65 | ); 66 | 67 | bytes32 imageId = keccak256(abi.encodePacked(image.PCR0, image.PCR1, image.PCR2)); 68 | require(whitelistedImages[imageId].PCR0.length == 0, "AA:WI-image already whitelisted"); 69 | whitelistedImages[imageId] = EnclaveImage(image.PCR0, image.PCR1, image.PCR2); 70 | emit EnclaveImageWhitelisted(imageId, image.PCR0, image.PCR1, image.PCR2); 71 | return imageId; 72 | } 73 | 74 | function _revokeEnclaveImage(bytes32 imageId) internal { 75 | require(whitelistedImages[imageId].PCR0.length != 0, "AA:RI-Image not whitelisted"); 76 | 77 | delete whitelistedImages[imageId]; 78 | emit EnclaveImageRevoked(imageId); 79 | } 80 | 81 | function _whitelistEnclaveKey(bytes memory enclavePubKey, bytes32 imageId) internal { 82 | require(whitelistedImages[imageId].PCR0.length != 0, "AA:WK-Image not whitelisted"); 83 | address enclaveKey = _pubKeyToAddress(enclavePubKey); 84 | require(verifiedKeys[enclaveKey] == bytes32(0), "AA:WK-Enclave key already verified"); 85 | 86 | verifiedKeys[enclaveKey] = imageId; 87 | emit EnclaveKeyWhitelisted(enclavePubKey, imageId); 88 | } 89 | 90 | function _revokeEnclaveKey(bytes memory enclavePubKey) internal { 91 | address enclaveKey = _pubKeyToAddress(enclavePubKey); 92 | require(verifiedKeys[enclaveKey] != bytes32(0), "AA:RK-Enclave key not verified"); 93 | 94 | delete verifiedKeys[enclaveKey]; 95 | emit EnclaveKeyRevoked(enclavePubKey); 96 | } 97 | 98 | // add enclave key of a whitelisted image to the list of verified enclave keys 99 | function _verifyKey( 100 | bytes memory signature, 101 | bytes memory enclavePubKey, 102 | bytes32 imageId, 103 | uint256 enclaveCPUs, 104 | uint256 enclaveMemory, 105 | uint256 timestampInMilliseconds 106 | ) internal { 107 | require( 108 | whitelistedImages[imageId].PCR0.length != 0, 109 | "AA:VK-Enclave image to verify not whitelisted" 110 | ); 111 | address enclaveKey = _pubKeyToAddress(enclavePubKey); 112 | require( 113 | verifiedKeys[enclaveKey] == bytes32(0), 114 | "AA:VK-Enclave key already verified" 115 | ); 116 | require(timestampInMilliseconds / 1000 > block.timestamp - ATTESTATION_MAX_AGE , "AA:VK-Attestation too old"); 117 | 118 | EnclaveImage memory image = whitelistedImages[imageId]; 119 | ATTESTATION_VERIFIER.verify( 120 | signature, 121 | enclavePubKey, 122 | image.PCR0, 123 | image.PCR1, 124 | image.PCR2, 125 | enclaveCPUs, 126 | enclaveMemory, 127 | timestampInMilliseconds 128 | ); 129 | 130 | verifiedKeys[enclaveKey] = imageId; 131 | emit EnclaveKeyVerified(enclavePubKey, imageId); 132 | } 133 | 134 | function verifyKey( 135 | bytes memory signature, 136 | bytes memory enclavePubKey, 137 | bytes32 imageId, 138 | uint256 enclaveCPUs, 139 | uint256 enclaveMemory, 140 | uint256 timestampInMilliseconds 141 | ) external { 142 | return _verifyKey(signature, enclavePubKey, imageId, enclaveCPUs, enclaveMemory, timestampInMilliseconds); 143 | } 144 | 145 | function _allowOnlyVerified(address key) internal view { 146 | bytes32 imageId = verifiedKeys[key]; 147 | require( 148 | imageId != bytes32(0), 149 | "AA:AOV-Enclave key must be verified" 150 | ); 151 | require( 152 | whitelistedImages[imageId].PCR0.length != 0, 153 | "AA:AOV-Source image must be whitelisted" 154 | ); 155 | } 156 | 157 | function getWhitelistedImage(bytes32 _imageId) external view returns (EnclaveImage memory) { 158 | return whitelistedImages[_imageId]; 159 | } 160 | 161 | function getVerifiedKey(address _key) external view returns (bytes32) { 162 | return verifiedKeys[_key]; 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /.openzeppelin/unknown-250.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifestVersion": "3.2", 3 | "proxies": [ 4 | { 5 | "address": "0x8Fbf3759cDe693B19493663a4ef5853728Cb71f8", 6 | "txHash": "0x4dc8a96794220abb131bd1711e57fcb560bd9807eb0ab175541a0b5e1667beaf", 7 | "kind": "uups" 8 | } 9 | ], 10 | "impls": { 11 | "f74e386a47270dc8ad76030b5e74c58303f7c75ee85780a66c052c820e41ee51": { 12 | "address": "0x1718169637cE9998010bB4D65830d36faA426F00", 13 | "txHash": "0xc9ca09561c4dd0fc999f2fc30c8945afef55db57384bd7636cc25f53a694a209", 14 | "layout": { 15 | "storage": [ 16 | { 17 | "contract": "Initializable", 18 | "label": "_initialized", 19 | "type": "t_bool", 20 | "src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:39" 21 | }, 22 | { 23 | "contract": "Initializable", 24 | "label": "_initializing", 25 | "type": "t_bool", 26 | "src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:44" 27 | }, 28 | { 29 | "contract": "ContextUpgradeable", 30 | "label": "__gap", 31 | "type": "t_array(t_uint256)50_storage", 32 | "src": "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol:31" 33 | }, 34 | { 35 | "contract": "ERC165Upgradeable", 36 | "label": "__gap", 37 | "type": "t_array(t_uint256)50_storage", 38 | "src": "@openzeppelin/contracts-upgradeable/utils/introspection/ERC165Upgradeable.sol:36" 39 | }, 40 | { 41 | "contract": "AccessControlUpgradeable", 42 | "label": "_roles", 43 | "type": "t_mapping(t_bytes32,t_struct(RoleData)199_storage)", 44 | "src": "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol:64" 45 | }, 46 | { 47 | "contract": "AccessControlUpgradeable", 48 | "label": "__gap", 49 | "type": "t_array(t_uint256)49_storage", 50 | "src": "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol:232" 51 | }, 52 | { 53 | "contract": "AccessControlEnumerableUpgradeable", 54 | "label": "_roleMembers", 55 | "type": "t_mapping(t_bytes32,t_struct(AddressSet)2957_storage)", 56 | "src": "@openzeppelin/contracts-upgradeable/access/AccessControlEnumerableUpgradeable.sol:26" 57 | }, 58 | { 59 | "contract": "AccessControlEnumerableUpgradeable", 60 | "label": "__gap", 61 | "type": "t_array(t_uint256)49_storage", 62 | "src": "@openzeppelin/contracts-upgradeable/access/AccessControlEnumerableUpgradeable.sol:74" 63 | }, 64 | { 65 | "contract": "ERC20Upgradeable", 66 | "label": "_balances", 67 | "type": "t_mapping(t_address,t_uint256)", 68 | "src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:37" 69 | }, 70 | { 71 | "contract": "ERC20Upgradeable", 72 | "label": "_allowances", 73 | "type": "t_mapping(t_address,t_mapping(t_address,t_uint256))", 74 | "src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:39" 75 | }, 76 | { 77 | "contract": "ERC20Upgradeable", 78 | "label": "_totalSupply", 79 | "type": "t_uint256", 80 | "src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:41" 81 | }, 82 | { 83 | "contract": "ERC20Upgradeable", 84 | "label": "_name", 85 | "type": "t_string_storage", 86 | "src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:43" 87 | }, 88 | { 89 | "contract": "ERC20Upgradeable", 90 | "label": "_symbol", 91 | "type": "t_string_storage", 92 | "src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:44" 93 | }, 94 | { 95 | "contract": "ERC20Upgradeable", 96 | "label": "__gap", 97 | "type": "t_array(t_uint256)45_storage", 98 | "src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:362" 99 | }, 100 | { 101 | "contract": "ERC20CappedUpgradeable", 102 | "label": "_cap", 103 | "type": "t_uint256", 104 | "src": "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20CappedUpgradeable.sol:13" 105 | }, 106 | { 107 | "contract": "ERC20CappedUpgradeable", 108 | "label": "__gap", 109 | "type": "t_array(t_uint256)50_storage", 110 | "src": "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20CappedUpgradeable.sol:43" 111 | }, 112 | { 113 | "contract": "ERC1967UpgradeUpgradeable", 114 | "label": "__gap", 115 | "type": "t_array(t_uint256)50_storage", 116 | "src": "@openzeppelin/contracts-upgradeable/proxy/ERC1967/ERC1967UpgradeUpgradeable.sol:215" 117 | }, 118 | { 119 | "contract": "UUPSUpgradeable", 120 | "label": "__gap", 121 | "type": "t_array(t_uint256)50_storage", 122 | "src": "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol:81" 123 | }, 124 | { 125 | "contract": "Pond", 126 | "label": "__gap0", 127 | "type": "t_array(t_uint256)500_storage", 128 | "src": "contracts/Pond.sol:27" 129 | } 130 | ], 131 | "types": { 132 | "t_array(t_uint256)500_storage": { 133 | "label": "uint256[500]" 134 | }, 135 | "t_uint256": { 136 | "label": "uint256" 137 | }, 138 | "t_array(t_uint256)50_storage": { 139 | "label": "uint256[50]" 140 | }, 141 | "t_mapping(t_address,t_uint256)": { 142 | "label": "mapping(address => uint256)" 143 | }, 144 | "t_address": { 145 | "label": "address" 146 | }, 147 | "t_mapping(t_address,t_mapping(t_address,t_uint256))": { 148 | "label": "mapping(address => mapping(address => uint256))" 149 | }, 150 | "t_string_storage": { 151 | "label": "string" 152 | }, 153 | "t_array(t_uint256)45_storage": { 154 | "label": "uint256[45]" 155 | }, 156 | "t_mapping(t_bytes32,t_struct(AddressSet)2957_storage)": { 157 | "label": "mapping(bytes32 => struct EnumerableSetUpgradeable.AddressSet)" 158 | }, 159 | "t_bytes32": { 160 | "label": "bytes32" 161 | }, 162 | "t_struct(AddressSet)2957_storage": { 163 | "label": "struct EnumerableSetUpgradeable.AddressSet", 164 | "members": [ 165 | { 166 | "label": "_inner", 167 | "type": "t_struct(Set)2656_storage" 168 | } 169 | ] 170 | }, 171 | "t_struct(Set)2656_storage": { 172 | "label": "struct EnumerableSetUpgradeable.Set", 173 | "members": [ 174 | { 175 | "label": "_values", 176 | "type": "t_array(t_bytes32)dyn_storage" 177 | }, 178 | { 179 | "label": "_indexes", 180 | "type": "t_mapping(t_bytes32,t_uint256)" 181 | } 182 | ] 183 | }, 184 | "t_array(t_bytes32)dyn_storage": { 185 | "label": "bytes32[]" 186 | }, 187 | "t_mapping(t_bytes32,t_uint256)": { 188 | "label": "mapping(bytes32 => uint256)" 189 | }, 190 | "t_array(t_uint256)49_storage": { 191 | "label": "uint256[49]" 192 | }, 193 | "t_mapping(t_bytes32,t_struct(RoleData)199_storage)": { 194 | "label": "mapping(bytes32 => struct AccessControlUpgradeable.RoleData)" 195 | }, 196 | "t_struct(RoleData)199_storage": { 197 | "label": "struct AccessControlUpgradeable.RoleData", 198 | "members": [ 199 | { 200 | "label": "members", 201 | "type": "t_mapping(t_address,t_bool)" 202 | }, 203 | { 204 | "label": "adminRole", 205 | "type": "t_bytes32" 206 | } 207 | ] 208 | }, 209 | "t_mapping(t_address,t_bool)": { 210 | "label": "mapping(address => bool)" 211 | }, 212 | "t_bool": { 213 | "label": "bool" 214 | } 215 | } 216 | } 217 | } 218 | } 219 | } 220 | --------------------------------------------------------------------------------