├── .gitignore ├── .openzeppelin ├── base-sepolia.json ├── base.json └── sepolia.json ├── .prettierrc.json ├── README.md ├── contracts ├── AgentInference.sol ├── AgentRewardV2.sol ├── AgentRewardV3.sol ├── IAgentReward.sol ├── IAgentRewardV3.sol ├── acp │ ├── ACPSimple.sol │ └── InteractionLedger.sol ├── contribution │ ├── ContributionNft.sol │ ├── IContributionNft.sol │ ├── IServiceNft.sol │ └── ServiceNft.sol ├── dev │ ├── BMWToken.sol │ ├── BMWTokenChild.sol │ ├── ERC6551BytecodeLib.sol │ ├── ERC6551Registry.sol │ ├── FxERC20ChildTunnel.sol │ ├── FxERC20RootTunnel.sol │ ├── ProxyAdmin.sol │ ├── tba │ │ └── lib │ │ │ ├── ERC6551AccountLib.sol │ │ │ └── ERC6551BytecodeLib.sol │ └── veVirtualToken.sol ├── fun │ ├── Bonding.sol │ ├── FERC20.sol │ ├── FFactory.sol │ ├── FPair.sol │ ├── FRouter.sol │ └── IFPair.sol ├── genesis │ ├── FGenesis.sol │ ├── Genesis.sol │ ├── GenesisLib.sol │ ├── GenesisTypes.sol │ ├── MockAgentFactoryV3.sol │ └── MockERC20.sol ├── governance │ ├── Defender.sol │ ├── GovernorCountingSimple.sol │ ├── GovernorCountingVP.sol │ ├── VirtualGenesisDAO.sol │ ├── VirtualProtocolDAO.sol │ └── VirtualProtocolDAOV2.sol ├── libs │ ├── AddressCheckpoints.sol │ ├── Elo.sol │ ├── FixedPointMathLib.sol │ ├── IERC6551Registry.sol │ ├── RewardSettingsCheckpoints.sol │ ├── RewardSettingsCheckpointsV2.sol │ └── TokenSaver.sol ├── pool │ ├── AeroAdaptor.sol │ ├── IRouter.sol │ ├── IUniswapV2Factory.sol │ ├── IUniswapV2Pair.sol │ ├── IUniswapV2Router01.sol │ └── IUniswapV2Router02.sol ├── tax │ ├── AgentTax.sol │ ├── BondingTax.sol │ ├── IBondingTax.sol │ ├── ITBABonus.sol │ ├── LPRefund.sol │ ├── TBABonus.sol │ └── TaxSwapper.sol ├── token │ ├── Airdrop.sol │ ├── IMinter.sol │ ├── IVEVirtual.sol │ ├── Minter.sol │ ├── StakedToken.sol │ ├── Virtual.sol │ ├── veViewer.sol │ └── veVirtual.sol └── virtualPersona │ ├── AgentDAO.sol │ ├── AgentFactory.sol │ ├── AgentFactoryV3.sol │ ├── AgentFactoryV4.sol │ ├── AgentMigrator.sol │ ├── AgentNftV2.sol │ ├── AgentToken.sol │ ├── AgentVeToken.sol │ ├── CoreRegistry.sol │ ├── ERC20Votes.sol │ ├── EloCalculator.sol │ ├── GovernorCountingSimpleUpgradeable.sol │ ├── IAgentDAO.sol │ ├── IAgentFactory.sol │ ├── IAgentFactoryV3.sol │ ├── IAgentFactoryV4.sol │ ├── IAgentNft.sol │ ├── IAgentToken.sol │ ├── IAgentVeToken.sol │ ├── IERC20Config.sol │ ├── IEloCalculator.sol │ ├── IErrors.sol │ ├── IExecutionInterface.sol │ ├── IValidatorRegistry.sol │ └── ValidatorRegistry.sol ├── funding.json ├── hardhat.config.js ├── package.json ├── scripts ├── arguments │ ├── acpArguments.js │ ├── aeroAdaptor.js │ ├── bmwArguments.js │ ├── bmwChild.js │ ├── bondingTax.js │ ├── contributionNft.js │ ├── elo.js │ ├── fgenesis.js │ ├── genesisDaoArguments.js │ ├── gov2.js │ ├── govArguments.js │ ├── inference.js │ ├── lockArguments.js │ ├── minter.js │ ├── nft.js │ ├── personaFactoryArguments.js │ ├── rewardTreasuryArguments.js │ ├── rewardsV2.js │ ├── serviceNft.js │ ├── staking.js │ ├── taxSwapper.js │ ├── tbaBonus.js │ ├── veTokenArguments.js │ └── vipFactory.js ├── deployDefender.ts ├── deployGovernance2.ts ├── deployTaxSwapper.ts ├── deployVeVirtual.ts ├── genesis │ └── deployGenesis.ts ├── society │ └── deployACP.ts ├── v1 │ ├── deployContributions.ts │ ├── deployGovernance.ts │ ├── deployPersonaFactory.ts │ ├── deployReward.ts │ ├── deployRewardTreasury.ts │ ├── deployRewardV2.ts │ ├── deployService.ts │ ├── deployTimeLock.ts │ ├── deployVetoken.ts │ ├── deployVirtualGenesis.ts │ ├── deployVirtualImplementations.ts │ ├── deployVirtualNft.ts │ └── upgradeFactory.ts └── v2 │ ├── configureTBABonus.ts │ ├── deployAeroAdaptor.ts │ ├── deployBondingTax.ts │ ├── deployElo.ts │ ├── deployFactory.ts │ ├── deployInference.ts │ ├── deployMigrator.ts │ ├── deployMinter.ts │ ├── deployRewards.ts │ ├── deployStaking.ts │ ├── deployTBABonus.ts │ ├── deployVIPFactory.ts │ ├── migrateRewards.ts │ ├── migrateV2.ts │ ├── upgradeAgentTax.ts │ └── upgradeFRouter.ts ├── test ├── ProtocolDAO.js ├── agentDAO.js ├── bonding.js ├── contribution.js ├── delegate.js ├── deprecated │ ├── contribution.js.old │ ├── genesisDAO.js.old │ ├── rewards.js.old │ └── staking.js.old ├── elo.js ├── genesis │ ├── const.js │ ├── genesis.js │ ├── permission.js │ └── setup.js ├── inference.js ├── rewardsV2.js ├── vevirtual.js └── virtualGenesis.js ├── tsconfig.json ├── yarn-error.log └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .env* 3 | scripts/dev 4 | 5 | # OS generated files # 6 | ###################### 7 | .DS_Store 8 | .DS_Store? 9 | ._* 10 | .Spotlight-V100 11 | .Trashes 12 | ehthumbs.db 13 | Thumbs.db 14 | 15 | # RUNTTIME files # 16 | ################## 17 | cache/ 18 | artifacts/ 19 | deployments 20 | fireblocks_secret.key 21 | .cursor/ -------------------------------------------------------------------------------- /.openzeppelin/sepolia.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifestVersion": "3.2", 3 | "proxies": [ 4 | { 5 | "address": "0x1D7aAf461d4899F3805cBBb80BAa38721F9b09f3", 6 | "txHash": "0x91de2a646ed1c17ec074d89e64601c86babd2a13e36e2d77a9a8d6e29087fcd0", 7 | "kind": "transparent" 8 | } 9 | ], 10 | "impls": { 11 | "eef29393abb381c7752a18b1564567d07a6caae3119b351d098a9c2654dab7a6": { 12 | "address": "0x1257519B0c825F39b7Bb9f3820d311192a709f57", 13 | "txHash": "0x8ed68815284a70c4fa223a3be46cb1a681a743e0cdb4ca01819b823d8018d72f", 14 | "layout": { 15 | "solcVersion": "0.8.20", 16 | "storage": [ 17 | { 18 | "label": "thenum", 19 | "offset": 0, 20 | "slot": "0", 21 | "type": "t_uint256", 22 | "contract": "Box", 23 | "src": "contracts/dev/Box.sol:6" 24 | } 25 | ], 26 | "types": { 27 | "t_bool": { 28 | "label": "bool", 29 | "numberOfBytes": "1" 30 | }, 31 | "t_struct(InitializableStorage)10_storage": { 32 | "label": "struct Initializable.InitializableStorage", 33 | "members": [ 34 | { 35 | "label": "_initialized", 36 | "type": "t_uint64", 37 | "offset": 0, 38 | "slot": "0" 39 | }, 40 | { 41 | "label": "_initializing", 42 | "type": "t_bool", 43 | "offset": 8, 44 | "slot": "0" 45 | } 46 | ], 47 | "numberOfBytes": "32" 48 | }, 49 | "t_uint64": { 50 | "label": "uint64", 51 | "numberOfBytes": "8" 52 | }, 53 | "t_uint256": { 54 | "label": "uint256", 55 | "numberOfBytes": "32" 56 | } 57 | }, 58 | "namespaces": { 59 | "erc7201:openzeppelin.storage.Initializable": [ 60 | { 61 | "contract": "Initializable", 62 | "label": "_initialized", 63 | "type": "t_uint64", 64 | "src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:69", 65 | "offset": 0, 66 | "slot": "0" 67 | }, 68 | { 69 | "contract": "Initializable", 70 | "label": "_initializing", 71 | "type": "t_bool", 72 | "src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:73", 73 | "offset": 8, 74 | "slot": "0" 75 | } 76 | ] 77 | } 78 | } 79 | }, 80 | "8a2584de7e0294db434e51a9e2c355595a84f7c52ae2fa4940e7449125d0d99c": { 81 | "address": "0x9672BCA0B58EAF4E41aE3DD34AE4d01CAF038180", 82 | "txHash": "0x7fdb5bf294f25fad983a3a07dd197b2486db998d26f1d72cb9c38e77b49c927b", 83 | "layout": { 84 | "solcVersion": "0.8.20", 85 | "storage": [ 86 | { 87 | "label": "thenum", 88 | "offset": 0, 89 | "slot": "0", 90 | "type": "t_uint256", 91 | "contract": "Box2", 92 | "src": "contracts/dev/Box2.sol:6" 93 | } 94 | ], 95 | "types": { 96 | "t_bool": { 97 | "label": "bool", 98 | "numberOfBytes": "1" 99 | }, 100 | "t_struct(InitializableStorage)10_storage": { 101 | "label": "struct Initializable.InitializableStorage", 102 | "members": [ 103 | { 104 | "label": "_initialized", 105 | "type": "t_uint64", 106 | "offset": 0, 107 | "slot": "0" 108 | }, 109 | { 110 | "label": "_initializing", 111 | "type": "t_bool", 112 | "offset": 8, 113 | "slot": "0" 114 | } 115 | ], 116 | "numberOfBytes": "32" 117 | }, 118 | "t_uint64": { 119 | "label": "uint64", 120 | "numberOfBytes": "8" 121 | }, 122 | "t_uint256": { 123 | "label": "uint256", 124 | "numberOfBytes": "32" 125 | } 126 | }, 127 | "namespaces": { 128 | "erc7201:openzeppelin.storage.Initializable": [ 129 | { 130 | "contract": "Initializable", 131 | "label": "_initialized", 132 | "type": "t_uint64", 133 | "src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:69", 134 | "offset": 0, 135 | "slot": "0" 136 | }, 137 | { 138 | "contract": "Initializable", 139 | "label": "_initializing", 140 | "type": "t_bool", 141 | "src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:73", 142 | "offset": 8, 143 | "slot": "0" 144 | } 145 | ] 146 | } 147 | } 148 | } 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "overrides": [ 3 | { 4 | "files": "*.sol", 5 | "options": { 6 | "printWidth": 80, 7 | "tabWidth": 4, 8 | "useTabs": false, 9 | "singleQuote": false, 10 | "bracketSpacing": false 11 | } 12 | }, 13 | { 14 | "files": "*.yml", 15 | "options": {} 16 | }, 17 | { 18 | "files": "*.yaml", 19 | "options": {} 20 | }, 21 | { 22 | "files": "*.toml", 23 | "options": {} 24 | }, 25 | { 26 | "files": "*.json", 27 | "options": {} 28 | }, 29 | { 30 | "files": "*.js", 31 | "options": {} 32 | }, 33 | { 34 | "files": "*.ts", 35 | "options": {} 36 | } 37 | ] 38 | } 39 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Virtual Protocol Contracts 3 | 4 | 5 | 6 | | Contract | Purpose | Access Control | Upgradable | 7 | | ------ | ------ | ------ | ------ | 8 | | veVirtualToken | This is a non-transferrable voting token to be used to vote on Virtual Protocol DAO and Virtual Genesis DAO | Ownable | N | 9 | | VirtualProtocolDAO | Regular DAO to maintain the VIRTUAL ecosystem | - | N | 10 | | VirtualGenesisDAO | Used to vote for instantiation of a VIRTUAL. This DAO allows early execution of proposal as soon as quorum (10k votes) is reached. | - | N | 11 | | AgentFactory | Handles the application & instantiation of a new VIRTUAL. References to TBA registry, VIRTUAL DAO/Token implementation and Persona NFT vault contracts are stored here. | Roles : DEFAULT_ADMIN_ROLE, WITHDRAW_ROLE | Y | 12 | | AgentNft | This is the main registry for Persona, Core and Validator. Used to generate ICV wallet address. | Roles: DEFAULT_ADMIN_ROLE, VALIDATOR_ADMIN_ROLE, MINTER_ROLE | Y | 13 | | ContributionNft | Each contribution will mint a new ContributionNft. Anyone can propose a new contribution at the VIRTUAL DAO and mint token using the proposal Id. | - | Y | 14 | | ServiceNft | Accepted contribution will mint a ServiceNft, restricted to only VIRTUAL DAO can mint a ServiceNft. User can query the latest service NFT for a VIRTUAL CORE. | - | Y | 15 | | AgentToken | This is implementation contract for VIRTUAL staking. AgentFactory will clone this during VIRTUAL instantiation. Staked token is non-transferable. | - | N | 16 | | AgentDAO | This is implementation contract for VIRTUAL specific DAO. AgentFactory will clone this during VIRTUAL instantiation. It holds the maturity score for each core service. | - | N | 17 | | AgentReward | This is reward distribution center. | Roles: GOV_ROLE, TOKEN_SAVER_ROLE | Y | 18 | | TimeLockStaking | Allows user to stake their VIRTUAL in exchange for sVIRTUAL | Roles: GOV_ROLE, TOKEN_SAVER_ROLE | N | 19 | | Virtual | VIRTUAL token | Ownable | N | 20 | | Airdrop | Airdrop token to holders | - | N | 21 | 22 | 23 | # Main Activities 24 | ## VIRTUAL Genesis 25 | 1. Submit a new application at **AgentFactory** 26 | a. It will transfer VIRTUAL to AgentFactory 27 | 2. Propose at **VirtualGenesisDAO** (action = ```VirtualFactory.executeApplication``` ) 28 | 3. Start voting at **VirtualGenesisDAO** 29 | 4. Execute proposal at **VirtualGenesisDAO** , it will do following: 30 | a. Clone **AgentToken** 31 | b. Clone **AgentDAO** 32 | c. Mint **AgentNft** 33 | d. Stake VIRTUAL -> $PERSONA (depending on the symbol sent to application) 34 | e. Create **TBA** with **AgentNft** 35 | 36 | 37 | ## Submit Contribution 38 | 1. Create proposal at **AgentDAO** (action = ServiceNft.mint) 39 | 2. Mint **ContributionNft** , it will authenticate by checking whether sender is the proposal's proposer. 40 | 41 | 42 | ## Upgrading Core 43 | 1. Validator vote for contribution proposal at **AgentDAO** 44 | 2. Execute proposal at **AgentDAO**, it will mint a **ServiceNft**, and trigger following actions: 45 | a. Update maturity score 46 | b. Update VIRTUAL core service id. 47 | 48 | 49 | ## Distribute Reward 50 | 1. On daily basis, protocol backend will conclude daily profits into a single amount. 51 | 2. Protocol backend calls **AgentReward**.distributeRewards , triggering following: 52 | a. Transfer VIRTUAL into **AgentReward** 53 | b. Account & update claimable amounts for: Protocol, Stakers, Validators, Dataset Contributors, Model Contributors 54 | 55 | 56 | ## Claim Reward 57 | 1. Protocol calls **AgentReward**.withdrawProtocolRewards 58 | 2. Stakers, Validators, Dataset Contributors, Model Contributors calls **AgentReward**.claimAllRewards 59 | 60 | 61 | ## Staking VIRTUAL 62 | 1. Call **AgentToken**.stake , pass in the validator that you would like to delegate your voting power to. It will take in sVIRTUAL and mint $*PERSONA* to you. 63 | 2. Call **AgentToken**.withdraw to withdraw , will burn your $*PERSONA* and return sVIRTUAL to you. -------------------------------------------------------------------------------- /contracts/AgentInference.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.20; 3 | import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; 4 | import "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol"; 5 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 6 | import "@openzeppelin/contracts/utils/math/Math.sol"; 7 | import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; 8 | import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol"; 9 | import "@openzeppelin/contracts/utils/math/Math.sol"; 10 | import "./virtualPersona/IAgentNft.sol"; 11 | 12 | contract AgentInference is 13 | Initializable, 14 | AccessControlUpgradeable, 15 | ReentrancyGuardUpgradeable 16 | { 17 | using Math for uint256; 18 | using SafeERC20 for IERC20; 19 | 20 | bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE"); 21 | mapping(uint256 agentId => uint256) public inferenceCount; // Inference count per agent 22 | 23 | IERC20 public token; 24 | IAgentNft public agentNft; 25 | 26 | event Prompt( 27 | address indexed sender, 28 | bytes32 promptHash, 29 | uint256 agentId, 30 | uint256 cost, 31 | uint8[] coreIds 32 | ); 33 | 34 | function initialize( 35 | address defaultAdmin_, 36 | address token_, 37 | address agentNft_ 38 | ) external initializer { 39 | __AccessControl_init(); 40 | __ReentrancyGuard_init(); 41 | 42 | _grantRole(ADMIN_ROLE, defaultAdmin_); 43 | _grantRole(DEFAULT_ADMIN_ROLE, defaultAdmin_); 44 | 45 | token = IERC20(token_); 46 | agentNft = IAgentNft(agentNft_); 47 | } 48 | 49 | function prompt( 50 | bytes32 promptHash, 51 | uint256[] memory agentIds, 52 | uint256[] memory amounts, 53 | uint8[][] memory coreIds 54 | ) public nonReentrant { 55 | address sender = _msgSender(); 56 | uint256 total = 0; 57 | 58 | require( 59 | agentIds.length == amounts.length && 60 | agentIds.length == coreIds.length, 61 | "Invalid input" 62 | ); 63 | 64 | for (uint256 i = 0; i < amounts.length; i++) { 65 | total += amounts[i]; 66 | } 67 | 68 | require(token.balanceOf(sender) >= total, "Insufficient balance"); 69 | 70 | for (uint256 i = 0; i < agentIds.length; i++) { 71 | uint256 agentId = agentIds[i]; 72 | address agentTba = agentNft.virtualInfo(agentId).tba; 73 | token.safeTransferFrom(sender, agentTba, amounts[i]); 74 | 75 | inferenceCount[agentId]++; 76 | emit Prompt(sender, promptHash, agentId, amounts[i], coreIds[i]); 77 | } 78 | } 79 | 80 | function promptMulti( 81 | bytes32[] memory promptHashes, 82 | uint256[] memory agentIds, 83 | uint256[] memory amounts, 84 | uint8[][] memory coreIds 85 | ) public nonReentrant { 86 | address sender = _msgSender(); 87 | uint256 total = 0; 88 | uint256 len = agentIds.length; 89 | 90 | require( 91 | len == amounts.length && 92 | len == coreIds.length && 93 | len == promptHashes.length, 94 | "Invalid input" 95 | ); 96 | 97 | for (uint256 i = 0; i < len; i++) { 98 | total += amounts[i]; 99 | } 100 | 101 | require(token.balanceOf(sender) >= total, "Insufficient balance"); 102 | 103 | uint256 prevAgentId = 0; 104 | address agentTba = address(0); 105 | for (uint256 i = 0; i < len; i++) { 106 | uint256 agentId = agentIds[i]; 107 | if (prevAgentId != agentId) { 108 | agentTba = agentNft.virtualInfo(agentId).tba; 109 | } 110 | token.safeTransferFrom(sender, agentTba, amounts[i]); 111 | 112 | inferenceCount[agentId]++; 113 | emit Prompt( 114 | sender, 115 | promptHashes[i], 116 | agentId, 117 | amounts[i], 118 | coreIds[i] 119 | ); 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /contracts/IAgentReward.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.20; 3 | 4 | interface IAgentReward { 5 | struct MainReward { 6 | uint32 blockNumber; 7 | uint256 amount; 8 | uint256 agentCount; 9 | uint256 totalStaked; 10 | } 11 | 12 | // Virtual specific reward, the amount will be shared between validator pool and contributor pool 13 | // Validator pool will be shared by validators and stakers 14 | // Contributor pool will be shared by contribution NFT holders 15 | struct Reward { 16 | uint48 id; 17 | uint32 mainIndex; 18 | uint256 totalStaked; 19 | uint256 validatorAmount; 20 | uint256 contributorAmount; 21 | uint256 coreAmount; // Rewards per core 22 | } 23 | 24 | struct Claim { 25 | uint256 totalClaimed; 26 | uint32 rewardCount; // Track number of reward blocks claimed to avoid reclaiming 27 | } 28 | 29 | struct ServiceReward { 30 | uint256 impact; 31 | uint256 amount; 32 | uint256 parentAmount; 33 | uint256 totalClaimed; 34 | uint256 totalClaimedParent; 35 | } 36 | 37 | event NewMainReward( 38 | uint32 indexed pos, 39 | uint256 amount, 40 | uint256 agentCount, 41 | uint256 totalStaked 42 | ); 43 | 44 | event RewardSettingsUpdated( 45 | uint16 protocolShares, 46 | uint16 contributorShares, 47 | uint16 stakerShares, 48 | uint16 parentShares, 49 | uint256 stakeThreshold 50 | ); 51 | 52 | event RefContractsUpdated( 53 | address rewardToken, 54 | address agentNft, 55 | address contributionNft, 56 | address serviceNft 57 | ); 58 | 59 | event StakeThresholdUpdated(uint256 threshold); 60 | 61 | event ParentSharesUpdated(uint256 shares); 62 | 63 | event StakerRewardClaimed( 64 | uint256 virtualId, 65 | uint256 amount, 66 | address staker 67 | ); 68 | 69 | event ValidatorRewardClaimed( 70 | uint256 virtualId, 71 | uint256 amount, 72 | address validator 73 | ); 74 | 75 | event ServiceRewardsClaimed( 76 | uint256 nftId, 77 | address account, 78 | uint256 total, 79 | uint256 childrenAmount 80 | ); 81 | 82 | event NewAgentReward( 83 | uint32 mainIndex, 84 | uint256 virtualId, 85 | uint256 validatorAmount, 86 | uint256 contributorAmount, 87 | uint256 coreAmount 88 | ); 89 | 90 | event DatasetRewardsClaimed(uint256 nftId, address account, uint256 total); 91 | 92 | error ERC5805FutureLookup(uint256 timepoint, uint32 clock); 93 | 94 | error NotGovError(); 95 | 96 | error NotOwnerError(); 97 | } 98 | -------------------------------------------------------------------------------- /contracts/IAgentRewardV3.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.20; 3 | 4 | interface IAgentRewardV3 { 5 | struct Reward { 6 | uint256 blockNumber; 7 | uint256 amount; 8 | uint256[] lpValues; 9 | uint256[] virtualIds; 10 | } 11 | 12 | // Agent specific reward, the amount will be shared between stakers and validators 13 | struct AgentReward { 14 | uint256 id; 15 | uint256 rewardIndex; 16 | uint256 stakerAmount; 17 | uint256 validatorAmount; 18 | uint256 totalProposals; 19 | uint256 totalStaked; 20 | } 21 | 22 | struct Claim { 23 | uint256 totalClaimed; 24 | uint256 rewardCount; // Track number of reward blocks claimed to avoid reclaiming 25 | } 26 | 27 | event NewReward(uint256 pos, uint256[] virtualIds); 28 | 29 | event NewAgentReward(uint256 indexed virtualId, uint256 id); 30 | 31 | event RewardSettingsUpdated(uint16 protocolShares, uint16 stakerShares); 32 | 33 | event RefContractsUpdated(address rewardToken, address agentNft); 34 | 35 | event StakerRewardClaimed( 36 | uint256 indexed virtualId, 37 | address indexed staker, 38 | uint256 numRewards, 39 | uint256 amount 40 | ); 41 | 42 | event ValidatorRewardClaimed( 43 | uint256 indexed virtualId, 44 | address indexed validator, 45 | uint256 amount 46 | ); 47 | 48 | error ERC5805FutureLookup(uint256 timepoint, uint32 clock); 49 | 50 | error NotGovError(); 51 | 52 | error NotOwnerError(); 53 | } 54 | -------------------------------------------------------------------------------- /contracts/acp/InteractionLedger.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // This is sample implementation of ATIP to handle selling of tokens/ NFT 3 | pragma solidity ^0.8.20; 4 | 5 | abstract contract InteractionLedger { 6 | struct Memo { 7 | string content; 8 | MemoType memoType; 9 | bool isSecured; 10 | uint8 nextPhase; 11 | uint256 jobId; 12 | address sender; 13 | } 14 | uint256 public memoCounter; 15 | 16 | mapping(uint256 memoId => mapping(address signer => uint8 res)) 17 | public signatories; 18 | 19 | enum MemoType { 20 | MESSAGE, 21 | CONTEXT_URL, 22 | IMAGE_URL, 23 | VOICE_URL, 24 | OBJECT_URL, 25 | TXHASH 26 | } 27 | 28 | mapping(uint256 => Memo) public memos; 29 | 30 | event NewMemo( 31 | uint256 indexed jobId, 32 | address indexed sender, 33 | uint256 memoId 34 | ); 35 | event MemoSigned(uint256 memoId, bool isApproved, string reason); 36 | 37 | function _createMemo( 38 | uint256 jobId, 39 | string memory content, 40 | MemoType memoType, 41 | bool isSecured, 42 | uint8 nextPhase 43 | ) internal returns (uint256) { 44 | uint256 newMemoId = ++memoCounter; 45 | memos[newMemoId] = Memo({ 46 | content: content, 47 | memoType: memoType, 48 | isSecured: isSecured, 49 | nextPhase: nextPhase, 50 | jobId: jobId, 51 | sender: msg.sender 52 | }); 53 | 54 | emit NewMemo(jobId, msg.sender, newMemoId); 55 | 56 | return newMemoId; 57 | } 58 | 59 | function signMemo( 60 | uint256 memoId, 61 | bool isApproved, 62 | string memory reason 63 | ) public virtual; 64 | } 65 | -------------------------------------------------------------------------------- /contracts/contribution/IContributionNft.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.20; 3 | 4 | import "@openzeppelin/contracts/governance/IGovernor.sol"; 5 | 6 | interface IContributionNft { 7 | function tokenVirtualId(uint256 tokenId) external view returns (uint256); 8 | function tokenURI(uint256 tokenId) external view returns (string memory); 9 | function getChildren(uint256 tokenId) external view returns (uint256[] memory); 10 | function getParentId(uint256 tokenId) external view returns (uint256); 11 | function getCore(uint256 tokenId) external view returns (uint8); 12 | function isModel(uint256 tokenId) external view returns (bool); 13 | function getAdmin() external view returns (address); 14 | function getDatasetId(uint256 tokenId) external view returns (uint256); 15 | function getAgentDAO(uint256 virtualId) external view returns (IGovernor); 16 | function getEloCalculator() external view returns (address); 17 | } 18 | -------------------------------------------------------------------------------- /contracts/contribution/IServiceNft.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.20; 3 | 4 | interface IServiceNft { 5 | 6 | function getCore(uint256 tokenId) external view returns (uint8); 7 | 8 | function getMaturity(uint256 tokenId) external view returns (uint256); 9 | 10 | function getImpact(uint256 tokenId) external view returns (uint256); 11 | 12 | function getCoreService( 13 | uint256 virtualId, 14 | uint8 coreType 15 | ) external view returns (uint256); 16 | 17 | function getCoreDatasetAt( 18 | uint256 virtualId, 19 | uint8 coreType, 20 | uint256 index 21 | ) external view returns (uint256); 22 | 23 | function totalCoreDatasets( 24 | uint256 virtualId, 25 | uint8 coreType 26 | ) external view returns (uint256); 27 | 28 | function getCoreDatasets( 29 | uint256 virtualId, 30 | uint8 coreType 31 | ) external view returns (uint256[] memory); 32 | 33 | event CoreServiceUpdated( 34 | uint256 virtualId, 35 | uint8 coreType, 36 | uint256 serviceId 37 | ); 38 | 39 | event NewService( 40 | uint256 tokenId, 41 | uint8 coreId, 42 | uint256 maturity, 43 | uint256 impact, 44 | bool isModel 45 | ); 46 | 47 | event DatasetImpactUpdated(uint16 weight); 48 | 49 | event SetServiceScore(uint256 serviceId, uint256 eloRating, uint256 impact); 50 | } 51 | -------------------------------------------------------------------------------- /contracts/dev/BMWToken.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.20; 3 | 4 | import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; 5 | import "@openzeppelin/contracts/access/Ownable.sol"; 6 | import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol"; 7 | 8 | contract BMWToken is ERC20, Ownable, ERC20Permit { 9 | constructor(address initialOwner) 10 | ERC20("BeemerToken", "BMW") 11 | Ownable(initialOwner) 12 | ERC20Permit("BeemerToken") 13 | {} 14 | 15 | function mint(address to, uint256 amount) public { 16 | _mint(to, amount); 17 | } 18 | } -------------------------------------------------------------------------------- /contracts/dev/BMWTokenChild.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.20; 3 | 4 | import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; 5 | import "@openzeppelin/contracts/access/Ownable.sol"; 6 | import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol"; 7 | 8 | contract BMWTokenChild is ERC20 { 9 | address internal _fxManager; 10 | 11 | constructor( 12 | address fxManager 13 | ) 14 | ERC20("BeemerToken", "BMW") 15 | { 16 | _fxManager = fxManager; 17 | } 18 | 19 | function setFxManager(address fxManager) public { 20 | _fxManager = fxManager; 21 | } 22 | 23 | function mint(address user, uint256 amount) public { 24 | require(msg.sender == _fxManager, "Invalid sender"); 25 | _mint(user, amount); 26 | } 27 | 28 | function burn(address user, uint256 amount) public { 29 | require(msg.sender == _fxManager, "Invalid sender"); 30 | _burn(user, amount); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /contracts/dev/ERC6551BytecodeLib.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | library ERC6551BytecodeLib { 5 | function getCreationCode( 6 | address implementation_, 7 | uint256 chainId_, 8 | address tokenContract_, 9 | uint256 tokenId_, 10 | uint256 salt_ 11 | ) internal pure returns (bytes memory) { 12 | return 13 | abi.encodePacked( 14 | hex"3d60ad80600a3d3981f3363d3d373d3d3d363d73", 15 | implementation_, 16 | hex"5af43d82803e903d91602b57fd5bf3", 17 | abi.encode(salt_, chainId_, tokenContract_, tokenId_) 18 | ); 19 | } 20 | } -------------------------------------------------------------------------------- /contracts/dev/ERC6551Registry.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | // This is a fake ERC6551 Registry 3 | pragma solidity ^0.8.0; 4 | 5 | import "@openzeppelin/contracts/utils/Create2.sol"; 6 | 7 | import "../libs/IERC6551Registry.sol"; 8 | import "./ERC6551BytecodeLib.sol"; 9 | 10 | contract ERC6551Registry is IERC6551Registry { 11 | error InitializationFailed(); 12 | 13 | function createAccount( 14 | address implementation, 15 | bytes32 salt, 16 | uint256 chainId, 17 | address tokenContract, 18 | uint256 tokenId 19 | ) external returns (address) { 20 | return 0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc; 21 | } 22 | 23 | function account( 24 | address implementation, 25 | uint256 chainId, 26 | address tokenContract, 27 | uint256 tokenId, 28 | uint256 salt 29 | ) external view returns (address) { 30 | return 0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /contracts/dev/FxERC20ChildTunnel.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // This is a testing contract, not for production use 3 | pragma solidity ^0.8.0; 4 | 5 | import "./BMWTokenChild.sol"; 6 | 7 | contract FxERC20ChildTunnel { 8 | // Bridge L1->L2 9 | function syncDeposit(address childToken, uint256 amount) public { 10 | BMWTokenChild childTokenContract = BMWTokenChild(childToken); 11 | childTokenContract.mint(msg.sender, amount); 12 | } 13 | 14 | // Bridge L2->L1 15 | function withdraw(address childToken, uint256 amount) public { 16 | BMWTokenChild childTokenContract = BMWTokenChild(childToken); 17 | childTokenContract.burn(msg.sender, amount); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /contracts/dev/FxERC20RootTunnel.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | import {SafeERC20, IERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; 5 | 6 | contract FxERC20RootTunnel { 7 | using SafeERC20 for IERC20; 8 | 9 | function deposit(address rootToken, uint256 amount) public { 10 | // transfer from depositor to this contract 11 | IERC20(rootToken).safeTransferFrom( 12 | msg.sender, // depositor 13 | address(this), // manager contract 14 | amount 15 | ); 16 | } 17 | 18 | // exit processor 19 | function syncWithdraw(address rootToken, uint256 amount) public { 20 | // transfer from tokens to 21 | IERC20(rootToken).safeTransfer(msg.sender, amount); 22 | } 23 | } -------------------------------------------------------------------------------- /contracts/dev/ProxyAdmin.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.20; 2 | 3 | import "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; 4 | 5 | contract MyProxyAdmin is ProxyAdmin { 6 | constructor(address initialOwner) ProxyAdmin(initialOwner) {} 7 | } 8 | -------------------------------------------------------------------------------- /contracts/dev/tba/lib/ERC6551AccountLib.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | import "@openzeppelin/contracts/utils/Create2.sol"; 5 | import "./ERC6551BytecodeLib.sol"; 6 | 7 | library ERC6551AccountLib { 8 | function computeAddress( 9 | address registry, 10 | address _implementation, 11 | bytes32 _salt, 12 | uint256 chainId, 13 | address tokenContract, 14 | uint256 tokenId 15 | ) internal pure returns (address) { 16 | bytes32 bytecodeHash = keccak256( 17 | ERC6551BytecodeLib.getCreationCode( 18 | _implementation, _salt, chainId, tokenContract, tokenId 19 | ) 20 | ); 21 | 22 | return Create2.computeAddress(_salt, bytecodeHash, registry); 23 | } 24 | 25 | function isERC6551Account(address account, address expectedImplementation, address registry) 26 | internal 27 | view 28 | returns (bool) 29 | { 30 | // invalid bytecode size 31 | if (account.code.length != 0xAD) return false; 32 | 33 | address _implementation = implementation(account); 34 | 35 | // implementation does not exist 36 | if (_implementation.code.length == 0) return false; 37 | 38 | // invalid implementation 39 | if (_implementation != expectedImplementation) return false; 40 | 41 | (bytes32 _salt, uint256 chainId, address tokenContract, uint256 tokenId) = context(account); 42 | 43 | return account 44 | == computeAddress(registry, _implementation, _salt, chainId, tokenContract, tokenId); 45 | } 46 | 47 | function implementation(address account) internal view returns (address _implementation) { 48 | assembly { 49 | // copy proxy implementation (0x14 bytes) 50 | extcodecopy(account, 0xC, 0xA, 0x14) 51 | _implementation := mload(0x00) 52 | } 53 | } 54 | 55 | function implementation() internal view returns (address _implementation) { 56 | return implementation(address(this)); 57 | } 58 | 59 | function token(address account) internal view returns (uint256, address, uint256) { 60 | bytes memory encodedData = new bytes(0x60); 61 | 62 | assembly { 63 | // copy 0x60 bytes from end of context 64 | extcodecopy(account, add(encodedData, 0x20), 0x4d, 0x60) 65 | } 66 | 67 | return abi.decode(encodedData, (uint256, address, uint256)); 68 | } 69 | 70 | function token() internal view returns (uint256, address, uint256) { 71 | return token(address(this)); 72 | } 73 | 74 | function salt(address account) internal view returns (bytes32) { 75 | bytes memory encodedData = new bytes(0x20); 76 | 77 | assembly { 78 | // copy 0x20 bytes from beginning of context 79 | extcodecopy(account, add(encodedData, 0x20), 0x2d, 0x20) 80 | } 81 | 82 | return abi.decode(encodedData, (bytes32)); 83 | } 84 | 85 | function salt() internal view returns (bytes32) { 86 | return salt(address(this)); 87 | } 88 | 89 | function context(address account) internal view returns (bytes32, uint256, address, uint256) { 90 | bytes memory encodedData = new bytes(0x80); 91 | 92 | assembly { 93 | // copy full context (0x80 bytes) 94 | extcodecopy(account, add(encodedData, 0x20), 0x2D, 0x80) 95 | } 96 | 97 | return abi.decode(encodedData, (bytes32, uint256, address, uint256)); 98 | } 99 | 100 | function context() internal view returns (bytes32, uint256, address, uint256) { 101 | return context(address(this)); 102 | } 103 | } -------------------------------------------------------------------------------- /contracts/dev/tba/lib/ERC6551BytecodeLib.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.4; 3 | 4 | library ERC6551BytecodeLib { 5 | /** 6 | * @dev Returns the creation code of the token bound account for a non-fungible token. 7 | * 8 | * @return result The creation code of the token bound account 9 | */ 10 | function getCreationCode( 11 | address implementation, 12 | bytes32 salt, 13 | uint256 chainId, 14 | address tokenContract, 15 | uint256 tokenId 16 | ) internal pure returns (bytes memory result) { 17 | assembly { 18 | result := mload(0x40) // Grab the free memory pointer 19 | // Layout the variables and bytecode backwards 20 | mstore(add(result, 0xb7), tokenId) 21 | mstore(add(result, 0x97), shr(96, shl(96, tokenContract))) 22 | mstore(add(result, 0x77), chainId) 23 | mstore(add(result, 0x57), salt) 24 | mstore(add(result, 0x37), 0x5af43d82803e903d91602b57fd5bf3) 25 | mstore(add(result, 0x28), implementation) 26 | mstore(add(result, 0x14), 0x3d60ad80600a3d3981f3363d3d373d3d3d363d73) 27 | mstore(result, 0xb7) // Store the length 28 | mstore(0x40, add(result, 0xd7)) // Allocate the memory 29 | } 30 | } 31 | 32 | /** 33 | * @dev Returns the create2 address computed from `salt`, `bytecodeHash`, `deployer`. 34 | * 35 | * @return result The create2 address computed from `salt`, `bytecodeHash`, `deployer` 36 | */ 37 | function computeAddress(bytes32 salt, bytes32 bytecodeHash, address deployer) 38 | internal 39 | pure 40 | returns (address result) 41 | { 42 | assembly { 43 | result := mload(0x40) // Grab the free memory pointer 44 | mstore8(result, 0xff) 45 | mstore(add(result, 0x35), bytecodeHash) 46 | mstore(add(result, 0x01), shl(96, deployer)) 47 | mstore(add(result, 0x15), salt) 48 | result := keccak256(result, 0x55) 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /contracts/dev/veVirtualToken.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.20; 3 | 4 | import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; 5 | import "@openzeppelin/contracts/access/Ownable.sol"; 6 | import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Votes.sol"; 7 | import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol"; 8 | 9 | contract veVirtualToken is ERC20, ERC20Permit, ERC20Votes, Ownable { 10 | constructor( 11 | address initialOwner 12 | ) 13 | ERC20("Virtual Protocol Voting", "veVIRTUAL") 14 | ERC20Permit("Virtual Protocol Voting") 15 | Ownable(initialOwner) 16 | {} 17 | 18 | // Protocol oracle will call this function to sync token activities across multiple chains 19 | function oracleTransfer( 20 | address[] memory froms, 21 | address[] memory tos, 22 | uint256[] memory values 23 | ) external onlyOwner returns (bool) { 24 | require( 25 | froms.length == tos.length && tos.length == values.length, 26 | "Invalid input" 27 | ); 28 | for (uint256 i = 0; i < froms.length; i++) { 29 | _update(froms[i], tos[i], values[i]); 30 | } 31 | return true; 32 | } 33 | 34 | // Disable manual transfers 35 | 36 | function approve( 37 | address /*spender*/, 38 | uint256 /*value*/ 39 | ) public override returns (bool) { 40 | revert("Approve not supported"); 41 | } 42 | 43 | function transfer( 44 | address /*to*/, 45 | uint256 /*value*/ 46 | ) public override returns (bool) { 47 | revert("Transfer not supported"); 48 | } 49 | 50 | function transferFrom( 51 | address /*from*/, 52 | address /*to*/, 53 | uint256 /*value*/ 54 | ) public override returns (bool) { 55 | revert("Transfer not supported"); 56 | } 57 | 58 | // The following functions are overrides required by Solidity. 59 | 60 | function _update( 61 | address from, 62 | address to, 63 | uint256 value 64 | ) internal override(ERC20, ERC20Votes) { 65 | super._update(from, to, value); 66 | } 67 | 68 | function nonces( 69 | address owner 70 | ) public view override(ERC20Permit, Nonces) returns (uint256) { 71 | return super.nonces(owner); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /contracts/fun/FERC20.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.20; 3 | 4 | import "@openzeppelin/contracts/access/Ownable.sol"; 5 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 6 | 7 | contract FERC20 is Context, IERC20, Ownable { 8 | uint8 private constant _decimals = 18; 9 | 10 | uint256 private _totalSupply; 11 | 12 | string private _name; 13 | 14 | string private _symbol; 15 | 16 | uint public maxTx; 17 | 18 | uint256 private _maxTxAmount; 19 | 20 | mapping(address => uint256) private _balances; 21 | 22 | mapping(address => mapping(address => uint256)) private _allowances; 23 | 24 | mapping(address => bool) private isExcludedFromMaxTx; 25 | 26 | event MaxTxUpdated(uint _maxTx); 27 | 28 | constructor( 29 | string memory name_, 30 | string memory symbol_, 31 | uint256 supply, 32 | uint _maxTx 33 | ) Ownable(msg.sender) { 34 | _name = name_; 35 | 36 | _symbol = symbol_; 37 | 38 | _totalSupply = supply * 10 ** _decimals; 39 | 40 | _balances[_msgSender()] = _totalSupply; 41 | 42 | isExcludedFromMaxTx[_msgSender()] = true; 43 | 44 | isExcludedFromMaxTx[address(this)] = true; 45 | 46 | _updateMaxTx(_maxTx); 47 | 48 | emit Transfer(address(0), _msgSender(), _totalSupply); 49 | } 50 | 51 | function name() public view returns (string memory) { 52 | return _name; 53 | } 54 | 55 | function symbol() public view returns (string memory) { 56 | return _symbol; 57 | } 58 | 59 | function decimals() public pure returns (uint8) { 60 | return _decimals; 61 | } 62 | 63 | function totalSupply() public view override returns (uint256) { 64 | return _totalSupply; 65 | } 66 | 67 | function balanceOf(address account) public view override returns (uint256) { 68 | return _balances[account]; 69 | } 70 | 71 | function transfer( 72 | address recipient, 73 | uint256 amount 74 | ) public override returns (bool) { 75 | _transfer(_msgSender(), recipient, amount); 76 | 77 | return true; 78 | } 79 | 80 | function allowance( 81 | address owner, 82 | address spender 83 | ) public view override returns (uint256) { 84 | return _allowances[owner][spender]; 85 | } 86 | 87 | function approve( 88 | address spender, 89 | uint256 amount 90 | ) public override returns (bool) { 91 | _approve(_msgSender(), spender, amount); 92 | 93 | return true; 94 | } 95 | 96 | function transferFrom( 97 | address sender, 98 | address recipient, 99 | uint256 amount 100 | ) public override returns (bool) { 101 | _transfer(sender, recipient, amount); 102 | 103 | _approve( 104 | sender, 105 | _msgSender(), 106 | _allowances[sender][_msgSender()] - amount 107 | ); 108 | 109 | return true; 110 | } 111 | 112 | function _approve(address owner, address spender, uint256 amount) private { 113 | require(owner != address(0), "ERC20: approve from the zero address"); 114 | require(spender != address(0), "ERC20: approve to the zero address"); 115 | 116 | _allowances[owner][spender] = amount; 117 | 118 | emit Approval(owner, spender, amount); 119 | } 120 | 121 | function _transfer(address from, address to, uint256 amount) private { 122 | require(from != address(0), "ERC20: transfer from the zero address"); 123 | require(to != address(0), "ERC20: transfer to the zero address"); 124 | 125 | if (!isExcludedFromMaxTx[from]) { 126 | require(amount <= _maxTxAmount, "Exceeds MaxTx"); 127 | } 128 | 129 | _balances[from] = _balances[from] - amount; 130 | _balances[to] = _balances[to] + amount; 131 | 132 | emit Transfer(from, to, amount); 133 | } 134 | 135 | function _updateMaxTx(uint _maxTx) internal { 136 | maxTx = _maxTx; 137 | _maxTxAmount = (maxTx * _totalSupply) / 100; 138 | 139 | emit MaxTxUpdated(_maxTx); 140 | } 141 | 142 | function updateMaxTx(uint256 _maxTx) public onlyOwner { 143 | _updateMaxTx(_maxTx); 144 | } 145 | 146 | function excludeFromMaxTx(address user) public onlyOwner { 147 | require( 148 | user != address(0), 149 | "ERC20: Exclude Max Tx from the zero address" 150 | ); 151 | 152 | isExcludedFromMaxTx[user] = true; 153 | } 154 | 155 | function _burn(address user, uint256 amount) internal { 156 | require(user != address(0), "Invalid address"); 157 | _balances[user] = _balances[user] - amount; 158 | } 159 | 160 | function burnFrom(address user, uint256 amount) public onlyOwner { 161 | require(user != address(0), "Invalid address"); 162 | _balances[user] = _balances[user] - amount; 163 | emit Transfer(user, address(0), amount); 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /contracts/fun/FFactory.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.20; 3 | 4 | import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; 5 | import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol"; 6 | import "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol"; 7 | 8 | import "./FPair.sol"; 9 | 10 | contract FFactory is Initializable, AccessControlUpgradeable, ReentrancyGuardUpgradeable { 11 | bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE"); 12 | bytes32 public constant CREATOR_ROLE = keccak256("CREATOR_ROLE"); 13 | 14 | mapping(address => mapping(address => address)) private _pair; 15 | 16 | address[] public pairs; 17 | 18 | address public router; 19 | 20 | address public taxVault; 21 | uint256 public buyTax; 22 | uint256 public sellTax; 23 | 24 | event PairCreated( 25 | address indexed tokenA, 26 | address indexed tokenB, 27 | address pair, 28 | uint 29 | ); 30 | 31 | /// @custom:oz-upgrades-unsafe-allow constructor 32 | constructor() { 33 | _disableInitializers(); 34 | } 35 | 36 | function initialize( 37 | address taxVault_, 38 | uint256 buyTax_, 39 | uint256 sellTax_ 40 | ) external initializer { 41 | __AccessControl_init(); 42 | __ReentrancyGuard_init(); 43 | _grantRole(DEFAULT_ADMIN_ROLE, msg.sender); 44 | 45 | taxVault = taxVault_; 46 | buyTax = buyTax_; 47 | sellTax = sellTax_; 48 | } 49 | 50 | function _createPair( 51 | address tokenA, 52 | address tokenB 53 | ) internal returns (address) { 54 | require(tokenA != address(0), "Zero addresses are not allowed."); 55 | require(tokenB != address(0), "Zero addresses are not allowed."); 56 | require(router != address(0), "No router"); 57 | 58 | FPair pair_ = new FPair(router, tokenA, tokenB); 59 | 60 | _pair[tokenA][tokenB] = address(pair_); 61 | _pair[tokenB][tokenA] = address(pair_); 62 | 63 | pairs.push(address(pair_)); 64 | 65 | uint n = pairs.length; 66 | 67 | emit PairCreated(tokenA, tokenB, address(pair_), n); 68 | 69 | return address(pair_); 70 | } 71 | 72 | function createPair( 73 | address tokenA, 74 | address tokenB 75 | ) external onlyRole(CREATOR_ROLE) nonReentrant returns (address) { 76 | address pair = _createPair(tokenA, tokenB); 77 | 78 | return pair; 79 | } 80 | 81 | function getPair( 82 | address tokenA, 83 | address tokenB 84 | ) public view returns (address) { 85 | return _pair[tokenA][tokenB]; 86 | } 87 | 88 | function allPairsLength() public view returns (uint) { 89 | return pairs.length; 90 | } 91 | 92 | function setTaxParams( 93 | address newVault_, 94 | uint256 buyTax_, 95 | uint256 sellTax_ 96 | ) public onlyRole(ADMIN_ROLE) { 97 | require(newVault_ != address(0), "Zero addresses are not allowed."); 98 | 99 | taxVault = newVault_; 100 | buyTax = buyTax_; 101 | sellTax = sellTax_; 102 | } 103 | 104 | function setRouter(address router_) public onlyRole(ADMIN_ROLE) { 105 | router = router_; 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /contracts/fun/FPair.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.20; 3 | 4 | import "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; 5 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 6 | import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; 7 | 8 | import "./IFPair.sol"; 9 | 10 | contract FPair is IFPair, ReentrancyGuard { 11 | using SafeERC20 for IERC20; 12 | 13 | address public router; 14 | address public tokenA; 15 | address public tokenB; 16 | 17 | struct Pool { 18 | uint256 reserve0; 19 | uint256 reserve1; 20 | uint256 k; 21 | uint256 lastUpdated; 22 | } 23 | 24 | Pool private _pool; 25 | 26 | constructor(address router_, address token0, address token1) { 27 | require(router_ != address(0), "Zero addresses are not allowed."); 28 | require(token0 != address(0), "Zero addresses are not allowed."); 29 | require(token1 != address(0), "Zero addresses are not allowed."); 30 | 31 | router = router_; 32 | tokenA = token0; 33 | tokenB = token1; 34 | } 35 | 36 | modifier onlyRouter() { 37 | require(router == msg.sender, "Only router can call this function"); 38 | _; 39 | } 40 | 41 | event Mint(uint256 reserve0, uint256 reserve1); 42 | 43 | event Swap( 44 | uint256 amount0In, 45 | uint256 amount0Out, 46 | uint256 amount1In, 47 | uint256 amount1Out 48 | ); 49 | 50 | function mint( 51 | uint256 reserve0, 52 | uint256 reserve1 53 | ) public onlyRouter returns (bool) { 54 | require(_pool.lastUpdated == 0, "Already minted"); 55 | 56 | _pool = Pool({ 57 | reserve0: reserve0, 58 | reserve1: reserve1, 59 | k: reserve0 * reserve1, 60 | lastUpdated: block.timestamp 61 | }); 62 | 63 | emit Mint(reserve0, reserve1); 64 | 65 | return true; 66 | } 67 | 68 | function swap( 69 | uint256 amount0In, 70 | uint256 amount0Out, 71 | uint256 amount1In, 72 | uint256 amount1Out 73 | ) public onlyRouter returns (bool) { 74 | uint256 _reserve0 = (_pool.reserve0 + amount0In) - amount0Out; 75 | uint256 _reserve1 = (_pool.reserve1 + amount1In) - amount1Out; 76 | 77 | _pool = Pool({ 78 | reserve0: _reserve0, 79 | reserve1: _reserve1, 80 | k: _pool.k, 81 | lastUpdated: block.timestamp 82 | }); 83 | 84 | emit Swap(amount0In, amount0Out, amount1In, amount1Out); 85 | 86 | return true; 87 | } 88 | 89 | function approval( 90 | address _user, 91 | address _token, 92 | uint256 amount 93 | ) public onlyRouter returns (bool) { 94 | require(_user != address(0), "Zero addresses are not allowed."); 95 | require(_token != address(0), "Zero addresses are not allowed."); 96 | 97 | IERC20 token = IERC20(_token); 98 | 99 | token.forceApprove(_user, amount); 100 | 101 | return true; 102 | } 103 | 104 | function transferAsset( 105 | address recipient, 106 | uint256 amount 107 | ) public onlyRouter { 108 | require(recipient != address(0), "Zero addresses are not allowed."); 109 | 110 | IERC20(tokenB).safeTransfer(recipient, amount); 111 | } 112 | 113 | function transferTo(address recipient, uint256 amount) public onlyRouter { 114 | require(recipient != address(0), "Zero addresses are not allowed."); 115 | 116 | IERC20(tokenA).safeTransfer(recipient, amount); 117 | } 118 | 119 | function getReserves() public view returns (uint256, uint256) { 120 | return (_pool.reserve0, _pool.reserve1); 121 | } 122 | 123 | function kLast() public view returns (uint256) { 124 | return _pool.k; 125 | } 126 | 127 | function priceALast() public view returns (uint256) { 128 | return _pool.reserve1 / _pool.reserve0; 129 | } 130 | 131 | function priceBLast() public view returns (uint256) { 132 | return _pool.reserve0 / _pool.reserve1; 133 | } 134 | 135 | function balance() public view returns (uint256) { 136 | return IERC20(tokenA).balanceOf(address(this)); 137 | } 138 | 139 | function assetBalance() public view returns (uint256) { 140 | return IERC20(tokenB).balanceOf(address(this)); 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /contracts/fun/IFPair.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.20; 3 | 4 | interface IFPair { 5 | function getReserves() external view returns (uint256, uint256); 6 | 7 | function assetBalance() external view returns (uint256); 8 | 9 | function balance() external view returns (uint256); 10 | 11 | function mint(uint256 reserve0, uint256 reserve1) external returns (bool); 12 | 13 | function transferAsset(address recipient, uint256 amount) external; 14 | 15 | function transferTo(address recipient, uint256 amount) external; 16 | 17 | function swap( 18 | uint256 amount0In, 19 | uint256 amount0Out, 20 | uint256 amount1In, 21 | uint256 amount1Out 22 | ) external returns (bool); 23 | 24 | function kLast() external view returns (uint256); 25 | 26 | function approval( 27 | address _user, 28 | address _token, 29 | uint256 amount 30 | ) external returns (bool); 31 | } 32 | -------------------------------------------------------------------------------- /contracts/genesis/FGenesis.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.26; 3 | 4 | import {Genesis} from "./Genesis.sol"; 5 | import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol"; 6 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 7 | import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; 8 | import "./GenesisTypes.sol"; 9 | import "./GenesisLib.sol"; 10 | 11 | contract FGenesis is Initializable, AccessControlUpgradeable { 12 | using GenesisLib for *; 13 | 14 | bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE"); 15 | bytes32 public constant OPERATION_ROLE = keccak256("OPERATION_ROLE"); 16 | 17 | struct Params { 18 | address virtualToken; 19 | uint256 reserve; 20 | uint256 maxContribution; 21 | address feeAddr; 22 | uint256 feeAmt; 23 | uint256 duration; 24 | bytes32 tbaSalt; 25 | address tbaImpl; 26 | uint32 votePeriod; 27 | uint256 threshold; 28 | address agentFactory; 29 | uint256 agentTokenTotalSupply; 30 | uint256 agentTokenLpSupply; 31 | } 32 | 33 | Params public params; 34 | mapping(uint256 => address) public genesisContracts; 35 | uint256 public genesisID; 36 | 37 | event GenesisCreated(uint256 indexed id, address indexed addr); 38 | 39 | bytes32 public constant BONDING_ROLE = keccak256("BONDING_ROLE"); 40 | 41 | /// @custom:oz-upgrades-unsafe-allow constructor 42 | constructor() { 43 | _disableInitializers(); 44 | } 45 | 46 | function initialize(Params memory p) external initializer { 47 | __AccessControl_init(); 48 | _grantRole(DEFAULT_ADMIN_ROLE, msg.sender); 49 | _grantRole(ADMIN_ROLE, msg.sender); 50 | _setParams(p); 51 | } 52 | 53 | function setParams(Params calldata p) external onlyRole(ADMIN_ROLE) { 54 | _setParams(p); 55 | } 56 | 57 | function _setParams(Params memory p) internal { 58 | require( 59 | p.virtualToken != address(0) && 60 | p.feeAddr != address(0) && 61 | p.tbaImpl != address(0) && 62 | p.agentFactory != address(0), 63 | "Invalid addr" 64 | ); 65 | 66 | require( 67 | p.reserve > 0 && p.maxContribution > 0 && p.feeAmt > 0, 68 | "Invalid amt" 69 | ); 70 | 71 | require( 72 | p.duration > 0 && 73 | p.agentTokenTotalSupply > 0 && 74 | p.agentTokenLpSupply > 0 && 75 | p.agentTokenTotalSupply >= p.agentTokenLpSupply, 76 | "Invalid amt" 77 | ); 78 | 79 | params = p; 80 | } 81 | 82 | function createGenesis( 83 | GenesisCreationParams memory gParams 84 | ) external returns (address) { 85 | require( 86 | IERC20(params.virtualToken).transferFrom( 87 | msg.sender, 88 | params.feeAddr, 89 | params.feeAmt 90 | ), 91 | "transfer createGenesis fee failed" 92 | ); 93 | 94 | gParams.endTime = gParams.startTime + params.duration; 95 | genesisID++; 96 | address addr = GenesisLib.validateAndDeploy( 97 | genesisID, 98 | address(this), 99 | gParams, 100 | params.tbaSalt, 101 | params.tbaImpl, 102 | params.votePeriod, 103 | params.threshold, 104 | params.agentFactory, 105 | params.virtualToken, 106 | params.reserve, 107 | params.maxContribution, 108 | params.agentTokenTotalSupply, 109 | params.agentTokenLpSupply 110 | ); 111 | 112 | IAccessControl(params.agentFactory).grantRole( 113 | BONDING_ROLE, 114 | address(addr) 115 | ); 116 | 117 | genesisContracts[genesisID] = addr; 118 | emit GenesisCreated(genesisID, addr); 119 | return addr; 120 | } 121 | 122 | function _getGenesis(uint256 id) internal view returns (Genesis) { 123 | address addr = genesisContracts[id]; 124 | require(addr != address(0), "Not found"); 125 | return Genesis(addr); 126 | } 127 | 128 | function onGenesisSuccess( 129 | uint256 id, 130 | SuccessParams calldata p 131 | ) external onlyRole(OPERATION_ROLE) returns (address) { 132 | return 133 | _getGenesis(id).onGenesisSuccess( 134 | p.refundAddresses, 135 | p.refundAmounts, 136 | p.distributeAddresses, 137 | p.distributeAmounts, 138 | p.creator 139 | ); 140 | } 141 | 142 | function onGenesisSuccessSalt( 143 | uint256 id, 144 | SuccessParams calldata p, 145 | bytes32 salt 146 | ) external onlyRole(OPERATION_ROLE) returns (address) { 147 | return 148 | _getGenesis(id).onGenesisSuccessSalt( 149 | p.refundAddresses, 150 | p.refundAmounts, 151 | p.distributeAddresses, 152 | p.distributeAmounts, 153 | p.creator, 154 | salt 155 | ); 156 | } 157 | 158 | function onGenesisFailed( 159 | uint256 id, 160 | uint256[] calldata participantIndexes 161 | ) external onlyRole(OPERATION_ROLE) { 162 | _getGenesis(id).onGenesisFailed(participantIndexes); 163 | } 164 | 165 | function withdrawLeftAssetsAfterFinalized( 166 | uint256 id, 167 | address to, 168 | address token, 169 | uint256 amount 170 | ) external onlyRole(ADMIN_ROLE) { 171 | _getGenesis(id).withdrawLeftAssetsAfterFinalized(to, token, amount); 172 | } 173 | 174 | function resetTime( 175 | uint256 id, 176 | uint256 newStartTime, 177 | uint256 newEndTime 178 | ) external onlyRole(OPERATION_ROLE) { 179 | _getGenesis(id).resetTime(newStartTime, newEndTime); 180 | } 181 | 182 | function cancelGenesis(uint256 id) external onlyRole(OPERATION_ROLE) { 183 | _getGenesis(id).cancelGenesis(); 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /contracts/genesis/GenesisLib.sol: -------------------------------------------------------------------------------- 1 | // GenesisLib.sol 2 | // SPDX-License-Identifier: MIT 3 | pragma solidity ^0.8.26; 4 | 5 | import {Genesis} from "./Genesis.sol"; 6 | import "./GenesisTypes.sol"; 7 | 8 | library GenesisLib { 9 | function validateAndDeploy( 10 | uint256 genesisID, 11 | address factory, 12 | GenesisCreationParams memory params, 13 | bytes32 tbaSalt, 14 | address tbaImpl, 15 | uint32 daoVotingPeriod, 16 | uint256 daoThreshold, 17 | address agentFactoryAddress, 18 | address virtualToken, 19 | uint256 reserve, 20 | uint256 maxContribution, 21 | uint256 agentTokenTotalSupply, 22 | uint256 agentTokenLpSupply 23 | ) internal returns (address) { 24 | require( 25 | bytes(params.genesisName).length > 0 && 26 | bytes(params.genesisTicker).length > 0 && 27 | params.genesisCores.length > 0, 28 | "Invalid params" 29 | ); 30 | 31 | Genesis newGenesis = new Genesis(); 32 | 33 | GenesisInitParams memory initParams = GenesisInitParams({ 34 | genesisID: genesisID, 35 | factory: factory, 36 | startTime: params.startTime, 37 | endTime: params.endTime, 38 | genesisName: params.genesisName, 39 | genesisTicker: params.genesisTicker, 40 | genesisCores: params.genesisCores, 41 | tbaSalt: tbaSalt, 42 | tbaImplementation: tbaImpl, 43 | daoVotingPeriod: daoVotingPeriod, 44 | daoThreshold: daoThreshold, 45 | agentFactoryAddress: agentFactoryAddress, 46 | virtualTokenAddress: virtualToken, 47 | reserveAmount: reserve, 48 | maxContributionVirtualAmount: maxContribution, 49 | agentTokenTotalSupply: agentTokenTotalSupply, 50 | agentTokenLpSupply: agentTokenLpSupply 51 | }); 52 | 53 | newGenesis.initialize(initParams); 54 | return address(newGenesis); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /contracts/genesis/GenesisTypes.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.26; 3 | 4 | struct GenesisCreationParams { 5 | uint256 startTime; 6 | uint256 endTime; 7 | string genesisName; 8 | string genesisTicker; 9 | uint8[] genesisCores; 10 | } 11 | 12 | struct SuccessParams { 13 | address[] refundAddresses; 14 | uint256[] refundAmounts; 15 | address[] distributeAddresses; 16 | uint256[] distributeAmounts; 17 | address creator; 18 | } 19 | 20 | struct GenesisInitParams { 21 | uint256 genesisID; 22 | address factory; 23 | uint256 startTime; 24 | uint256 endTime; 25 | string genesisName; 26 | string genesisTicker; 27 | uint8[] genesisCores; 28 | bytes32 tbaSalt; 29 | address tbaImplementation; 30 | uint32 daoVotingPeriod; 31 | uint256 daoThreshold; 32 | address agentFactoryAddress; 33 | address virtualTokenAddress; 34 | uint256 reserveAmount; 35 | uint256 maxContributionVirtualAmount; 36 | uint256 agentTokenTotalSupply; 37 | uint256 agentTokenLpSupply; 38 | } 39 | -------------------------------------------------------------------------------- /contracts/genesis/MockAgentFactoryV3.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.26; 3 | 4 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 5 | import "../virtualPersona/IAgentFactoryV3.sol"; 6 | import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; 7 | import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol"; 8 | import "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol"; 9 | 10 | contract MockAgentFactoryV3 is 11 | IAgentFactoryV3, 12 | Initializable, 13 | AccessControlUpgradeable, 14 | PausableUpgradeable 15 | { 16 | // Mock variables 17 | address public mockAgentToken; 18 | uint256 public mockId; 19 | bytes32 public constant BONDING_ROLE = keccak256("BONDING_ROLE"); 20 | 21 | /// @custom:oz-upgrades-unsafe-allow constructor 22 | constructor() { 23 | _disableInitializers(); 24 | } 25 | 26 | function initialize( 27 | address, // tokenImplementation 28 | address, // veTokenImplementation 29 | address, // daoImplementation 30 | address, // tbaRegistry 31 | address, // assetToken 32 | address, // nft 33 | uint256, // applicationThreshold 34 | address, // vault 35 | uint256 // nextId 36 | ) public initializer { 37 | __AccessControl_init(); 38 | __Pausable_init(); 39 | _grantRole(DEFAULT_ADMIN_ROLE, msg.sender); 40 | _grantRole(BONDING_ROLE, msg.sender); 41 | } 42 | 43 | // Mock setter functions 44 | function setMockAgentToken(address token) external { 45 | mockAgentToken = token; 46 | } 47 | 48 | function setMockId(uint256 id) external { 49 | mockId = id; 50 | } 51 | 52 | // Interface implementations 53 | function proposeAgent( 54 | string memory, 55 | string memory, 56 | string memory, 57 | uint8[] memory, 58 | bytes32, 59 | address, 60 | uint32, 61 | uint256 62 | ) public view returns (uint256) { 63 | return mockId; 64 | } 65 | 66 | function withdraw(uint256) public pure { 67 | // Mock implementation - do nothing 68 | } 69 | 70 | function totalAgents() public pure returns (uint256) { 71 | return 1; // Mock implementation returns 1 72 | } 73 | 74 | function initFromBondingCurve( 75 | string memory, 76 | string memory, 77 | uint8[] memory, 78 | bytes32, 79 | address, 80 | uint32, 81 | uint256, 82 | uint256, 83 | address 84 | ) public view whenNotPaused onlyRole(BONDING_ROLE) returns (uint256) { 85 | return mockId; 86 | } 87 | 88 | function executeBondingCurveApplication( 89 | uint256, 90 | uint256, 91 | uint256, 92 | address 93 | ) public view onlyRole(BONDING_ROLE) returns (address) { 94 | return address(mockAgentToken); 95 | } 96 | 97 | function executeBondingCurveApplicationSalt( 98 | uint256, 99 | uint256, 100 | uint256, 101 | address, 102 | bytes32 103 | ) public view onlyRole(BONDING_ROLE) returns (address) { 104 | return address(mockAgentToken); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /contracts/genesis/MockERC20.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.26; 3 | 4 | import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; 5 | 6 | contract MockERC20 is ERC20 { 7 | constructor( 8 | string memory name, 9 | string memory symbol, 10 | address initialAccount, 11 | uint256 initialBalance 12 | ) ERC20(name, symbol) { 13 | _mint(initialAccount, initialBalance); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /contracts/governance/GovernorCountingSimple.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // OpenZeppelin Contracts (last updated v5.0.0) (governance/extensions/GovernorCountingSimple.sol) 3 | 4 | pragma solidity ^0.8.20; 5 | 6 | import "@openzeppelin/contracts/governance/Governor.sol"; 7 | 8 | /** 9 | * @dev Extension of {Governor} for simple, 4 options, vote counting. 10 | */ 11 | abstract contract GovernorCountingSimple is Governor { 12 | /** 13 | * @dev Supported vote types. Matches Governor Bravo ordering. 14 | */ 15 | enum VoteType { 16 | Against, 17 | For, 18 | Abstain, 19 | Deliberate 20 | } 21 | 22 | struct ProposalVote { 23 | uint256 againstVotes; 24 | uint256 forVotes; 25 | uint256 abstainVotes; 26 | mapping(address voter => bool) hasVoted; 27 | } 28 | 29 | mapping(uint256 proposalId => ProposalVote) private _proposalVotes; 30 | 31 | /** 32 | * @dev See {IGovernor-COUNTING_MODE}. 33 | */ 34 | // solhint-disable-next-line func-name-mixedcase 35 | function COUNTING_MODE() public pure virtual override returns (string memory) { 36 | return "support=bravo&quorum=for,abstain"; 37 | } 38 | 39 | /** 40 | * @dev See {IGovernor-hasVoted}. 41 | */ 42 | function hasVoted(uint256 proposalId, address account) public view virtual override returns (bool) { 43 | return _proposalVotes[proposalId].hasVoted[account]; 44 | } 45 | 46 | /** 47 | * @dev Accessor to the internal vote counts. 48 | */ 49 | function proposalVotes( 50 | uint256 proposalId 51 | ) public view virtual returns (uint256 againstVotes, uint256 forVotes, uint256 abstainVotes) { 52 | ProposalVote storage proposalVote = _proposalVotes[proposalId]; 53 | return (proposalVote.againstVotes, proposalVote.forVotes, proposalVote.abstainVotes); 54 | } 55 | 56 | /** 57 | * @dev See {Governor-_quorumReached}. 58 | */ 59 | function _quorumReached(uint256 proposalId) internal view virtual override returns (bool) { 60 | ProposalVote storage proposalVote = _proposalVotes[proposalId]; 61 | 62 | return quorum(proposalSnapshot(proposalId)) <= proposalVote.forVotes + proposalVote.abstainVotes; 63 | } 64 | 65 | /** 66 | * @dev See {Governor-_voteSucceeded}. In this module, the forVotes must be strictly over the againstVotes. 67 | */ 68 | function _voteSucceeded(uint256 proposalId) internal view virtual override returns (bool) { 69 | ProposalVote storage proposalVote = _proposalVotes[proposalId]; 70 | 71 | return proposalVote.forVotes > proposalVote.againstVotes; 72 | } 73 | 74 | /** 75 | * @dev See {Governor-_countVote}. In this module, the support follows the `VoteType` enum (from Governor Bravo). 76 | */ 77 | function _countVote( 78 | uint256 proposalId, 79 | address account, 80 | uint8 support, 81 | uint256 weight, 82 | bytes memory // params 83 | ) internal virtual override { 84 | ProposalVote storage proposalVote = _proposalVotes[proposalId]; 85 | 86 | // Allow user to cast vote with opinion and type "Deliberate" to signal that they are not voting 87 | if(support == uint8(VoteType.Deliberate)) { 88 | return; 89 | } 90 | 91 | if (proposalVote.hasVoted[account]) { 92 | revert GovernorAlreadyCastVote(account); 93 | } 94 | 95 | proposalVote.hasVoted[account] = true; 96 | 97 | if (support == uint8(VoteType.Against)) { 98 | proposalVote.againstVotes += weight; 99 | } else if (support == uint8(VoteType.For)) { 100 | proposalVote.forVotes += weight; 101 | } else if (support == uint8(VoteType.Abstain)) { 102 | proposalVote.abstainVotes += weight; 103 | } else { 104 | revert GovernorInvalidVoteType(); 105 | } 106 | } 107 | } -------------------------------------------------------------------------------- /contracts/governance/GovernorCountingVP.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.20; 3 | 4 | import "@openzeppelin/contracts/governance/Governor.sol"; 5 | 6 | 7 | abstract contract GovernorCountingVP is Governor { 8 | 9 | enum VoteType { 10 | Against, 11 | For, 12 | Abstain, 13 | Deliberate 14 | } 15 | 16 | struct ProposalVote { 17 | uint256 againstVotes; 18 | uint256 forVotes; 19 | uint256 abstainVotes; 20 | mapping(address voter => bool) hasVoted; 21 | } 22 | 23 | mapping(uint256 proposalId => ProposalVote) private _proposalVotes; 24 | mapping(uint256 proposalId => uint256[]) private _lockIds; 25 | 26 | 27 | function COUNTING_MODE() public pure virtual override returns (string memory) { 28 | return "support=bravo&quorum=for,abstain"; 29 | } 30 | 31 | function hasVoted(uint256 proposalId, address account) public view virtual override returns (bool) { 32 | return _proposalVotes[proposalId].hasVoted[account]; 33 | } 34 | 35 | /** 36 | * @dev Accessor to the internal vote counts. 37 | */ 38 | function proposalVotes( 39 | uint256 proposalId 40 | ) public view virtual returns (uint256 againstVotes, uint256 forVotes, uint256 abstainVotes) { 41 | ProposalVote storage proposalVote = _proposalVotes[proposalId]; 42 | return (proposalVote.againstVotes, proposalVote.forVotes, proposalVote.abstainVotes); 43 | } 44 | 45 | /** 46 | * @dev See {Governor-_quorumReached}. 47 | */ 48 | function _quorumReached(uint256 proposalId) internal view virtual override returns (bool) { 49 | ProposalVote storage proposalVote = _proposalVotes[proposalId]; 50 | 51 | return quorum(proposalDeadline(proposalId)) <= proposalVote.forVotes + proposalVote.abstainVotes; 52 | } 53 | 54 | /** 55 | * @dev See {Governor-_voteSucceeded}. In this module, the forVotes must be strictly over the againstVotes. 56 | */ 57 | function _voteSucceeded(uint256 proposalId) internal view virtual override returns (bool) { 58 | ProposalVote storage proposalVote = _proposalVotes[proposalId]; 59 | 60 | return proposalVote.forVotes > proposalVote.againstVotes; 61 | } 62 | 63 | /** 64 | * @dev See {Governor-_countVote}. In this module, the support follows the `VoteType` enum (from Governor Bravo). 65 | */ 66 | function _countVote( 67 | uint256 proposalId, 68 | address account, 69 | uint8 support, 70 | uint256 weight, 71 | bytes memory // params 72 | ) internal virtual override { 73 | 74 | if(support == uint8(VoteType.Deliberate)) { 75 | return; 76 | } 77 | 78 | ProposalVote storage proposalVote = _proposalVotes[proposalId]; 79 | 80 | if (proposalVote.hasVoted[account]) { 81 | revert GovernorAlreadyCastVote(account); 82 | } 83 | 84 | proposalVote.hasVoted[account] = true; 85 | 86 | if (support == uint8(VoteType.Against)) { 87 | proposalVote.againstVotes += weight; 88 | } else if (support == uint8(VoteType.For)) { 89 | proposalVote.forVotes += weight; 90 | } else if (support == uint8(VoteType.Abstain)) { 91 | proposalVote.abstainVotes += weight; 92 | } else { 93 | revert GovernorInvalidVoteType(); 94 | } 95 | } 96 | } -------------------------------------------------------------------------------- /contracts/governance/VirtualGenesisDAO.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // This DAO allows early execution of proposal as soon as quorum is reached 3 | pragma solidity ^0.8.20; 4 | 5 | import "@openzeppelin/contracts/governance/Governor.sol"; 6 | import "@openzeppelin/contracts/governance/extensions/GovernorSettings.sol"; 7 | import "@openzeppelin/contracts/governance/extensions/GovernorStorage.sol"; 8 | import "@openzeppelin/contracts/governance/extensions/GovernorVotes.sol"; 9 | import "@openzeppelin/contracts/governance/extensions/GovernorVotesQuorumFraction.sol"; 10 | import "./GovernorCountingSimple.sol"; 11 | import {Checkpoints} from "@openzeppelin/contracts/utils/structs/Checkpoints.sol"; 12 | import "@openzeppelin/contracts/access/AccessControl.sol"; 13 | 14 | contract VirtualGenesisDAO is 15 | Governor, 16 | GovernorSettings, 17 | GovernorStorage, 18 | GovernorVotes, 19 | GovernorCountingSimple, 20 | AccessControl 21 | { 22 | using Checkpoints for Checkpoints.Trace224; 23 | 24 | mapping(uint256 => bool) _earlyExecutions; 25 | Checkpoints.Trace224 private _quorumCheckpoints; 26 | 27 | uint256 private _quorum; 28 | 29 | event QuorumUpdated(uint224 oldQuorum, uint224 newQuorum); 30 | 31 | bytes32 public constant EXECUTOR_ROLE = keccak256("EXECUTOR_ROLE"); 32 | 33 | constructor( 34 | IVotes token, 35 | uint48 initialVotingDelay, 36 | uint32 initialVotingPeriod, 37 | uint256 initialProposalThreshold 38 | ) 39 | Governor("VirtualGenesis") 40 | GovernorSettings( 41 | initialVotingDelay, 42 | initialVotingPeriod, 43 | initialProposalThreshold 44 | ) 45 | GovernorVotes(token) 46 | { 47 | _quorumCheckpoints.push(0, 10000e18); 48 | _grantRole(DEFAULT_ADMIN_ROLE, _msgSender()); 49 | } 50 | 51 | // The following functions are overrides required by Solidity. 52 | 53 | function votingDelay() 54 | public 55 | view 56 | override(Governor, GovernorSettings) 57 | returns (uint256) 58 | { 59 | return super.votingDelay(); 60 | } 61 | 62 | function votingPeriod() 63 | public 64 | view 65 | override(Governor, GovernorSettings) 66 | returns (uint256) 67 | { 68 | return super.votingPeriod(); 69 | } 70 | 71 | function proposalThreshold() 72 | public 73 | view 74 | override(Governor, GovernorSettings) 75 | returns (uint256) 76 | { 77 | return super.proposalThreshold(); 78 | } 79 | 80 | function propose( 81 | address[] memory targets, 82 | uint256[] memory values, 83 | bytes[] memory calldatas, 84 | string memory description 85 | ) public override returns (uint256) { 86 | return super.propose(targets, values, calldatas, description); 87 | } 88 | 89 | function _propose( 90 | address[] memory targets, 91 | uint256[] memory values, 92 | bytes[] memory calldatas, 93 | string memory description, 94 | address proposer 95 | ) internal override(Governor, GovernorStorage) returns (uint256) { 96 | return 97 | super._propose(targets, values, calldatas, description, proposer); 98 | } 99 | 100 | function quorum( 101 | uint256 blockNumber 102 | ) public view override returns (uint256) { 103 | uint256 length = _quorumCheckpoints.length(); 104 | 105 | // Optimistic search, check the latest checkpoint 106 | Checkpoints.Checkpoint224 memory latest = _quorumCheckpoints.at( 107 | SafeCast.toUint32(length - 1) 108 | ); 109 | uint48 latestKey = latest._key; 110 | uint224 latestValue = latest._value; 111 | if (latestKey <= blockNumber) { 112 | return latestValue; 113 | } 114 | 115 | // Otherwise, do the binary search 116 | return 117 | _quorumCheckpoints.upperLookupRecent( 118 | SafeCast.toUint32(blockNumber) 119 | ); 120 | } 121 | 122 | function earlyExecute( 123 | uint256 proposalId 124 | ) public payable onlyRole(EXECUTOR_ROLE) returns (uint256) { 125 | ( 126 | address[] memory targets, 127 | uint256[] memory values, 128 | bytes[] memory calldatas, 129 | bytes32 descriptionHash 130 | ) = proposalDetails(proposalId); 131 | 132 | require( 133 | state(proposalId) == ProposalState.Active && 134 | _voteSucceeded(proposalId) && 135 | _quorumReached(proposalId) && 136 | !_earlyExecutions[proposalId], 137 | "Proposal not ready for early execution" 138 | ); 139 | // avoid reentrancy 140 | _earlyExecutions[proposalId] = true; 141 | 142 | _executeOperations( 143 | proposalId, 144 | targets, 145 | values, 146 | calldatas, 147 | descriptionHash 148 | ); 149 | 150 | emit ProposalExecuted(proposalId); 151 | 152 | return proposalId; 153 | } 154 | 155 | // Handle early execution 156 | function state( 157 | uint256 proposalId 158 | ) public view override returns (ProposalState) { 159 | if (_earlyExecutions[proposalId]) { 160 | return ProposalState.Executed; 161 | } 162 | return super.state(proposalId); 163 | } 164 | 165 | function updateQuorum(uint224 newQuorum) public onlyGovernance { 166 | uint224 oldQuorum = _quorumCheckpoints.latest(); 167 | _quorumCheckpoints.push( 168 | SafeCast.toUint32(clock()), 169 | SafeCast.toUint208(newQuorum) 170 | ); 171 | emit QuorumUpdated(oldQuorum, newQuorum); 172 | } 173 | 174 | function supportsInterface( 175 | bytes4 interfaceId 176 | ) public view override(Governor, AccessControl) returns (bool) { 177 | return super.supportsInterface(interfaceId); 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /contracts/governance/VirtualProtocolDAO.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.20; 3 | 4 | import "@openzeppelin/contracts/governance/Governor.sol"; 5 | import "@openzeppelin/contracts/governance/extensions/GovernorSettings.sol"; 6 | import "@openzeppelin/contracts/governance/extensions/GovernorStorage.sol"; 7 | import "@openzeppelin/contracts/governance/extensions/GovernorVotes.sol"; 8 | import "@openzeppelin/contracts/governance/extensions/GovernorVotesQuorumFraction.sol"; 9 | import "./GovernorCountingSimple.sol"; 10 | 11 | contract VirtualProtocolDAO is 12 | Governor, 13 | GovernorSettings, 14 | GovernorStorage, 15 | GovernorVotes, 16 | GovernorCountingSimple, 17 | GovernorVotesQuorumFraction 18 | { 19 | constructor( 20 | IVotes token, 21 | uint48 initialVotingDelay, 22 | uint32 initialVotingPeriod, 23 | uint256 initialProposalThreshold, 24 | uint256 initialQuorumNumerator 25 | ) 26 | Governor("VirtualProtocol") 27 | GovernorSettings(initialVotingDelay, initialVotingPeriod, initialProposalThreshold) 28 | GovernorVotes(token) 29 | GovernorVotesQuorumFraction(initialQuorumNumerator) 30 | {} 31 | 32 | // The following functions are overrides required by Solidity. 33 | 34 | function votingDelay() 35 | public 36 | view 37 | override(Governor, GovernorSettings) 38 | returns (uint256) 39 | { 40 | return super.votingDelay(); 41 | } 42 | 43 | function votingPeriod() 44 | public 45 | view 46 | override(Governor, GovernorSettings) 47 | returns (uint256) 48 | { 49 | return super.votingPeriod(); 50 | } 51 | 52 | function proposalThreshold() 53 | public 54 | view 55 | override(Governor, GovernorSettings) 56 | returns (uint256) 57 | { 58 | return super.proposalThreshold(); 59 | } 60 | 61 | function propose( 62 | address[] memory targets, 63 | uint256[] memory values, 64 | bytes[] memory calldatas, 65 | string memory description 66 | ) public override returns (uint256) { 67 | return super.propose(targets, values, calldatas, description); 68 | } 69 | 70 | function _propose( 71 | address[] memory targets, 72 | uint256[] memory values, 73 | bytes[] memory calldatas, 74 | string memory description, 75 | address proposer 76 | ) internal override(Governor, GovernorStorage) returns (uint256) { 77 | return 78 | super._propose(targets, values, calldatas, description, proposer); 79 | } 80 | 81 | function quorum( 82 | uint256 blockNumber 83 | ) 84 | public 85 | view 86 | override(Governor, GovernorVotesQuorumFraction) 87 | returns (uint256) 88 | { 89 | return super.quorum(blockNumber); 90 | } 91 | 92 | function quorumDenominator() public pure override returns (uint256) { 93 | return 10000; 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /contracts/libs/AddressCheckpoints.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // This is a modification of OpenZeppelin Checkpoints.sol to support custom struct 3 | pragma solidity ^0.8.20; 4 | 5 | import {Math} from "@openzeppelin/contracts/utils/math/Math.sol"; 6 | 7 | library AddressCheckpoints { 8 | error CheckpointUnorderedInsertion(); 9 | 10 | struct Trace { 11 | Checkpoint[] _checkpoints; 12 | } 13 | 14 | struct Checkpoint { 15 | uint48 _key; 16 | address _value; 17 | } 18 | 19 | function push(Trace storage self, uint48 key, address value) internal { 20 | return _insert(self._checkpoints, key, value); 21 | } 22 | 23 | function _insert( 24 | Checkpoint[] storage self, 25 | uint48 key, 26 | address value 27 | ) private { 28 | uint256 pos = self.length; 29 | 30 | if (pos > 0) { 31 | Checkpoint memory last = self[pos - 1]; 32 | 33 | if (last._key > key) { 34 | revert CheckpointUnorderedInsertion(); 35 | } 36 | 37 | if (last._key == key) { 38 | self[pos - 1]._value = value; 39 | } else { 40 | self.push(Checkpoint({_key: key, _value: value})); 41 | } 42 | } else { 43 | self.push(Checkpoint({_key: key, _value: value})); 44 | } 45 | } 46 | 47 | function latest(Trace storage self) internal view returns (address) { 48 | uint256 pos = self._checkpoints.length; 49 | return pos == 0 ? address(0) : self._checkpoints[pos - 1]._value; 50 | } 51 | 52 | function upperLookupRecent( 53 | Trace storage self, 54 | uint48 key 55 | ) internal view returns (address) { 56 | uint256 len = self._checkpoints.length; 57 | 58 | uint256 low = 0; 59 | uint256 high = len; 60 | 61 | if (len > 5) { 62 | uint256 mid = len - Math.sqrt(len); 63 | if (key < self._checkpoints[mid]._key) { 64 | high = mid; 65 | } else { 66 | low = mid + 1; 67 | } 68 | } 69 | 70 | uint256 pos = _upperBinaryLookup(self._checkpoints, key, low, high); 71 | 72 | return pos == 0 ? address(0) : self._checkpoints[pos - 1]._value; 73 | } 74 | 75 | function _upperBinaryLookup( 76 | Checkpoint[] storage self, 77 | uint48 key, 78 | uint256 low, 79 | uint256 high 80 | ) private view returns (uint256) { 81 | while (low < high) { 82 | uint256 mid = Math.average(low, high); 83 | if (self[mid]._key > key) { 84 | high = mid; 85 | } else { 86 | low = mid + 1; 87 | } 88 | } 89 | return high; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /contracts/libs/Elo.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.13; 3 | 4 | import {FixedPointMathLib as fp} from "./FixedPointMathLib.sol"; 5 | 6 | library Elo { 7 | /// @notice Get the 16th root of a number, used in ELO calculations 8 | /// @dev Elo calculations require the 400th root (10 ^ (x / 400)), however this can be simplified to the 16th root (10 ^ ((x / 25) / 16)) 9 | function sixteenthRoot(uint256 x) internal pure returns (uint256) { 10 | return fp.sqrt(fp.sqrt(fp.sqrt(fp.sqrt(x)))); 11 | } 12 | 13 | /// @notice Calculates the change in ELO rating, after a given outcome. 14 | /// @param ratingA the ELO rating of the player A 15 | /// @param ratingB the ELO rating of the player B 16 | /// @param score the score of the player A, scaled by 100. 100 = win, 50 = draw, 0 = loss 17 | /// @param kFactor the k-factor or development multiplier used to calculate the change in ELO rating. 20 is the typical value 18 | /// @return change the change in ELO rating of player A, with 2 decimals of precision. 1501 = 15.01 ELO change 19 | /// @return negative the directional change of player A's ELO. Opposite sign for player B 20 | function ratingChange(uint256 ratingA, uint256 ratingB, uint256 score, uint256 kFactor) 21 | internal 22 | pure 23 | returns (uint256 change, bool negative) 24 | { 25 | uint256 _kFactor; // scaled up `kFactor` by 100 26 | bool _negative = ratingB < ratingA; 27 | uint256 ratingDiff; // absolute value difference between `ratingA` and `ratingB` 28 | 29 | unchecked { 30 | // scale up the inputs by a factor of 100 31 | // since our elo math is scaled up by 100 (to avoid low precision integer division) 32 | _kFactor = kFactor * 10_000; 33 | ratingDiff = _negative ? ratingA - ratingB : ratingB - ratingA; 34 | } 35 | 36 | // checks against overflow/underflow, discovered via fuzzing 37 | // large rating diffs leads to 10^ratingDiff being too large to fit in a uint256 38 | require(ratingDiff < 1126, "Rating difference too large"); 39 | // large rating diffs when applying the scale factor leads to underflow (800 - ratingDiff) 40 | if (_negative) require(ratingDiff < 800, "Rating difference too large"); 41 | 42 | // ---------------------------------------------------------------------- 43 | // Below, we'll be running simplified versions of the following formulas: 44 | // expected score = 1 / (1 + 10 ^ (ratingDiff / 400)) 45 | // elo change = kFactor * (score - expectedScore) 46 | 47 | uint256 n; // numerator of the power, with scaling, (numerator of `ratingDiff / 400`) 48 | uint256 _powered; // the value of 10 ^ numerator 49 | uint256 powered; // the value of 16th root of 10 ^ numerator (fully resolved 10 ^ (ratingDiff / 400)) 50 | uint256 kExpectedScore; // the expected score with K factor distributed 51 | uint256 kScore; // the actual score with K factor distributed 52 | 53 | unchecked { 54 | // apply offset of 800 to scale the result by 100 55 | n = _negative ? 800 - ratingDiff : 800 + ratingDiff; 56 | 57 | // (x / 400) is the same as ((x / 25) / 16)) 58 | _powered = fp.rpow(10, n / 25, 1); // divide by 25 to avoid reach uint256 max 59 | powered = sixteenthRoot(_powered); // x ^ (1 / 16) is the same as 16th root of x 60 | 61 | // given `change = kFactor * (score - expectedScore)` we can distribute kFactor to both terms 62 | kExpectedScore = _kFactor / (100 + powered); // both numerator and denominator scaled up by 100 63 | kScore = kFactor * score; // input score is already scaled up by 100 64 | 65 | // determines the sign of the ELO change 66 | negative = kScore < kExpectedScore; 67 | change = negative ? kExpectedScore - kScore : kScore - kExpectedScore; 68 | } 69 | } 70 | } -------------------------------------------------------------------------------- /contracts/libs/IERC6551Registry.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | interface IERC6551Registry { 5 | /** 6 | * @dev The registry SHALL emit the AccountCreated event upon successful account creation 7 | */ 8 | event AccountCreated( 9 | address account, 10 | address indexed implementation, 11 | uint256 chainId, 12 | address indexed tokenContract, 13 | uint256 indexed tokenId, 14 | uint256 salt 15 | ); 16 | 17 | /** 18 | * @dev Creates a token bound account for a non-fungible token 19 | * 20 | * If account has already been created, returns the account address without calling create2 21 | * 22 | * If initData is not empty and account has not yet been created, calls account with 23 | * provided initData after creation 24 | * 25 | * Emits AccountCreated event 26 | * 27 | * @return the address of the account 28 | */ 29 | function createAccount( 30 | address implementation, 31 | bytes32 salt, 32 | uint256 chainId, 33 | address tokenContract, 34 | uint256 tokenId 35 | ) external returns (address); 36 | 37 | /** 38 | * @dev Returns the computed token bound account address for a non-fungible token 39 | * 40 | * @return The computed address of the token bound account 41 | */ 42 | function account( 43 | address implementation, 44 | uint256 chainId, 45 | address tokenContract, 46 | uint256 tokenId, 47 | uint256 salt 48 | ) external view returns (address); 49 | } -------------------------------------------------------------------------------- /contracts/libs/RewardSettingsCheckpoints.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // This is a modification of OpenZeppelin Checkpoints.sol to support custom struct 3 | pragma solidity ^0.8.20; 4 | 5 | import {Math} from "@openzeppelin/contracts/utils/math/Math.sol"; 6 | 7 | library RewardSettingsCheckpoints { 8 | error CheckpointUnorderedInsertion(); 9 | 10 | struct Trace { 11 | Checkpoint[] _checkpoints; 12 | } 13 | 14 | struct RewardSettings { 15 | uint16 protocolShares; 16 | uint16 contributorShares; 17 | uint16 stakerShares; 18 | uint16 parentShares; // Optional rewards for contribution's parent 19 | uint256 stakeThreshold; // Each VIRTUAL will require minimum amount of staked tokens to be considered for rewards 20 | } 21 | 22 | struct Checkpoint { 23 | uint32 _key; 24 | RewardSettings _value; 25 | } 26 | 27 | function push( 28 | Trace storage self, 29 | uint32 key, 30 | RewardSettings memory value 31 | ) internal { 32 | return _insert(self._checkpoints, key, value); 33 | } 34 | 35 | function _insert( 36 | Checkpoint[] storage self, 37 | uint32 key, 38 | RewardSettings memory value 39 | ) private { 40 | uint256 pos = self.length; 41 | 42 | if (pos > 0) { 43 | Checkpoint memory last = self[pos - 1]; 44 | 45 | if (last._key > key) { 46 | revert CheckpointUnorderedInsertion(); 47 | } 48 | 49 | if (last._key == key) { 50 | self[pos - 1]._value = value; 51 | } else { 52 | self.push(Checkpoint({_key: key, _value: value})); 53 | } 54 | } else { 55 | self.push(Checkpoint({_key: key, _value: value})); 56 | } 57 | } 58 | 59 | function latest( 60 | Trace storage self 61 | ) internal view returns (RewardSettings memory) { 62 | uint256 pos = self._checkpoints.length; 63 | return 64 | pos == 0 65 | ? RewardSettings(0, 0, 0, 0, 0) 66 | : self._checkpoints[pos - 1]._value; 67 | } 68 | 69 | function upperLookupRecent( 70 | Trace storage self, 71 | uint32 key 72 | ) internal view returns (RewardSettings memory) { 73 | uint256 len = self._checkpoints.length; 74 | 75 | uint256 low = 0; 76 | uint256 high = len; 77 | 78 | if (len > 5) { 79 | uint256 mid = len - Math.sqrt(len); 80 | if (key < self._checkpoints[mid]._key) { 81 | high = mid; 82 | } else { 83 | low = mid + 1; 84 | } 85 | } 86 | 87 | uint256 pos = _upperBinaryLookup(self._checkpoints, key, low, high); 88 | 89 | return 90 | pos == 0 91 | ? RewardSettings(0, 0, 0, 0, 0) 92 | : self._checkpoints[pos - 1]._value; 93 | } 94 | 95 | function _upperBinaryLookup( 96 | Checkpoint[] storage self, 97 | uint32 key, 98 | uint256 low, 99 | uint256 high 100 | ) private view returns (uint256) { 101 | while (low < high) { 102 | uint256 mid = Math.average(low, high); 103 | if (self[mid]._key > key) { 104 | high = mid; 105 | } else { 106 | low = mid + 1; 107 | } 108 | } 109 | return high; 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /contracts/libs/RewardSettingsCheckpointsV2.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // This is a modification of OpenZeppelin Checkpoints.sol to support custom struct 3 | pragma solidity ^0.8.20; 4 | 5 | import {Math} from "@openzeppelin/contracts/utils/math/Math.sol"; 6 | 7 | library RewardSettingsCheckpointsV2 { 8 | error CheckpointUnorderedInsertion(); 9 | 10 | struct Trace { 11 | Checkpoint[] _checkpoints; 12 | } 13 | 14 | struct RewardSettings { 15 | uint16 protocolShares; 16 | uint16 stakerShares; 17 | } 18 | 19 | struct Checkpoint { 20 | uint32 _key; 21 | RewardSettings _value; 22 | } 23 | 24 | function push( 25 | Trace storage self, 26 | uint32 key, 27 | RewardSettings memory value 28 | ) internal { 29 | return _insert(self._checkpoints, key, value); 30 | } 31 | 32 | function _insert( 33 | Checkpoint[] storage self, 34 | uint32 key, 35 | RewardSettings memory value 36 | ) private { 37 | uint256 pos = self.length; 38 | 39 | if (pos > 0) { 40 | Checkpoint memory last = self[pos - 1]; 41 | 42 | if (last._key > key) { 43 | revert CheckpointUnorderedInsertion(); 44 | } 45 | 46 | if (last._key == key) { 47 | self[pos - 1]._value = value; 48 | } else { 49 | self.push(Checkpoint({_key: key, _value: value})); 50 | } 51 | } else { 52 | self.push(Checkpoint({_key: key, _value: value})); 53 | } 54 | } 55 | 56 | function latest( 57 | Trace storage self 58 | ) internal view returns (RewardSettings memory) { 59 | uint256 pos = self._checkpoints.length; 60 | return 61 | pos == 0 62 | ? RewardSettings(0, 0) 63 | : self._checkpoints[pos - 1]._value; 64 | } 65 | 66 | function upperLookupRecent( 67 | Trace storage self, 68 | uint32 key 69 | ) internal view returns (RewardSettings memory) { 70 | uint256 len = self._checkpoints.length; 71 | 72 | uint256 low = 0; 73 | uint256 high = len; 74 | 75 | if (len > 5) { 76 | uint256 mid = len - Math.sqrt(len); 77 | if (key < self._checkpoints[mid]._key) { 78 | high = mid; 79 | } else { 80 | low = mid + 1; 81 | } 82 | } 83 | 84 | uint256 pos = _upperBinaryLookup(self._checkpoints, key, low, high); 85 | 86 | return 87 | pos == 0 88 | ? RewardSettings(0, 0) 89 | : self._checkpoints[pos - 1]._value; 90 | } 91 | 92 | function _upperBinaryLookup( 93 | Checkpoint[] storage self, 94 | uint32 key, 95 | uint256 low, 96 | uint256 high 97 | ) private view returns (uint256) { 98 | while (low < high) { 99 | uint256 mid = Math.average(low, high); 100 | if (self[mid]._key > key) { 101 | high = mid; 102 | } else { 103 | low = mid + 1; 104 | } 105 | } 106 | return high; 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /contracts/libs/TokenSaver.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.20; 3 | 4 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 5 | import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; 6 | import "@openzeppelin/contracts/access/AccessControl.sol"; 7 | 8 | contract TokenSaver is AccessControl { 9 | using SafeERC20 for IERC20; 10 | 11 | bytes32 public constant TOKEN_SAVER_ROLE = keccak256("TOKEN_SAVER_ROLE"); 12 | 13 | event TokenSaved(address indexed by, address indexed receiver, address indexed token, uint256 amount); 14 | 15 | modifier onlyTokenSaver() { 16 | require(hasRole(TOKEN_SAVER_ROLE, _msgSender()), "TokenSaver.onlyTokenSaver: permission denied"); 17 | _; 18 | } 19 | 20 | function saveToken(address _token, address _receiver, uint256 _amount) external onlyTokenSaver { 21 | IERC20(_token).safeTransfer(_receiver, _amount); 22 | emit TokenSaved(_msgSender(), _receiver, _token, _amount); 23 | } 24 | 25 | } -------------------------------------------------------------------------------- /contracts/pool/AeroAdaptor.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // This is an adaptor to make aerodrome pool compatible with UniswapRouter v2 3 | pragma solidity ^0.8.20; 4 | import "./IRouter.sol"; 5 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 6 | import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; 7 | 8 | interface IAeroRouter { 9 | struct Route { 10 | address from; 11 | address to; 12 | bool stable; 13 | address factory; 14 | } 15 | 16 | function swapExactTokensForTokens( 17 | uint256 amountIn, 18 | uint256 amountOutMin, 19 | Route[] calldata routes, 20 | address to, 21 | uint256 deadline 22 | ) external returns (uint256[] memory amounts); 23 | 24 | function getAmountsOut( 25 | uint256 amountIn, 26 | Route[] memory routes 27 | ) external view returns (uint256[] memory amounts); 28 | } 29 | 30 | contract AeroAdaptor is IRouter { 31 | using SafeERC20 for IERC20; 32 | 33 | address public router; 34 | address public tokenIn; 35 | address public tokenOut; 36 | address public factory; 37 | 38 | constructor( 39 | address router_, 40 | address tokenIn_, 41 | address tokenOut_, 42 | address factory_ 43 | ) { 44 | router = router_; 45 | tokenIn = tokenIn_; 46 | tokenOut = tokenOut_; 47 | factory = factory_; 48 | IERC20(tokenIn).forceApprove(router_, type(uint256).max); 49 | } 50 | 51 | function swapExactTokensForTokens( 52 | uint amountIn, 53 | uint amountOutMin, 54 | address[] calldata path, 55 | address to, 56 | uint deadline 57 | ) external returns (uint[] memory amounts) { 58 | IAeroRouter.Route[] memory routes = new IAeroRouter.Route[](1); 59 | routes[0] = IAeroRouter.Route(tokenIn, tokenOut, false, factory); 60 | 61 | IERC20(tokenIn).safeTransferFrom(msg.sender, address(this), amountIn); 62 | 63 | amounts = IAeroRouter(router).swapExactTokensForTokens( 64 | amountIn, 65 | amountOutMin, 66 | routes, 67 | to, 68 | deadline 69 | ); 70 | } 71 | 72 | function getAmountsOut( 73 | uint amountIn, 74 | address[] calldata path 75 | ) external view returns (uint[] memory amounts) { 76 | IAeroRouter.Route[] memory routes = new IAeroRouter.Route[](1); 77 | routes[0] = IAeroRouter.Route(tokenIn, tokenOut, false, factory); 78 | return IAeroRouter(router).getAmountsOut(amountIn, routes); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /contracts/pool/IRouter.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.20; 3 | 4 | interface IRouter { 5 | function swapExactTokensForTokens( 6 | uint amountIn, 7 | uint amountOutMin, 8 | address[] calldata path, 9 | address to, 10 | uint deadline 11 | ) external returns (uint[] memory amounts); 12 | 13 | function getAmountsOut( 14 | uint amountIn, 15 | address[] calldata path 16 | ) external view returns (uint[] memory amounts); 17 | } 18 | -------------------------------------------------------------------------------- /contracts/pool/IUniswapV2Factory.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.5.0; 2 | 3 | interface IUniswapV2Factory { 4 | event PairCreated(address indexed token0, address indexed token1, address pair, uint); 5 | 6 | function feeTo() external view returns (address); 7 | function feeToSetter() external view returns (address); 8 | 9 | function getPair(address tokenA, address tokenB) external view returns (address pair); 10 | function allPairs(uint) external view returns (address pair); 11 | function allPairsLength() external view returns (uint); 12 | 13 | function createPair(address tokenA, address tokenB) external returns (address pair); 14 | 15 | function setFeeTo(address) external; 16 | function setFeeToSetter(address) external; 17 | } -------------------------------------------------------------------------------- /contracts/pool/IUniswapV2Pair.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.5.0; 2 | 3 | interface IUniswapV2Pair { 4 | event Approval(address indexed owner, address indexed spender, uint value); 5 | event Transfer(address indexed from, address indexed to, uint value); 6 | 7 | function name() external pure returns (string memory); 8 | function symbol() external pure returns (string memory); 9 | function decimals() external pure returns (uint8); 10 | function totalSupply() external view returns (uint); 11 | function balanceOf(address owner) external view returns (uint); 12 | function allowance(address owner, address spender) external view returns (uint); 13 | 14 | function approve(address spender, uint value) external returns (bool); 15 | function transfer(address to, uint value) external returns (bool); 16 | function transferFrom(address from, address to, uint value) external returns (bool); 17 | 18 | function DOMAIN_SEPARATOR() external view returns (bytes32); 19 | function PERMIT_TYPEHASH() external pure returns (bytes32); 20 | function nonces(address owner) external view returns (uint); 21 | 22 | function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external; 23 | 24 | event Mint(address indexed sender, uint amount0, uint amount1); 25 | event Burn(address indexed sender, uint amount0, uint amount1, address indexed to); 26 | event Swap( 27 | address indexed sender, 28 | uint amount0In, 29 | uint amount1In, 30 | uint amount0Out, 31 | uint amount1Out, 32 | address indexed to 33 | ); 34 | event Sync(uint112 reserve0, uint112 reserve1); 35 | 36 | function MINIMUM_LIQUIDITY() external pure returns (uint); 37 | function factory() external view returns (address); 38 | function token0() external view returns (address); 39 | function token1() external view returns (address); 40 | function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast); 41 | function price0CumulativeLast() external view returns (uint); 42 | function price1CumulativeLast() external view returns (uint); 43 | function kLast() external view returns (uint); 44 | 45 | function mint(address to) external returns (uint liquidity); 46 | function burn(address to) external returns (uint amount0, uint amount1); 47 | function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external; 48 | function skim(address to) external; 49 | function sync() external; 50 | 51 | function initialize(address, address) external; 52 | } -------------------------------------------------------------------------------- /contracts/pool/IUniswapV2Router01.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.6.2; 2 | 3 | interface IUniswapV2Router01 { 4 | function factory() external pure returns (address); 5 | function WETH() external pure returns (address); 6 | 7 | function addLiquidity( 8 | address tokenA, 9 | address tokenB, 10 | uint amountADesired, 11 | uint amountBDesired, 12 | uint amountAMin, 13 | uint amountBMin, 14 | address to, 15 | uint deadline 16 | ) external returns (uint amountA, uint amountB, uint liquidity); 17 | function addLiquidityETH( 18 | address token, 19 | uint amountTokenDesired, 20 | uint amountTokenMin, 21 | uint amountETHMin, 22 | address to, 23 | uint deadline 24 | ) external payable returns (uint amountToken, uint amountETH, uint liquidity); 25 | function removeLiquidity( 26 | address tokenA, 27 | address tokenB, 28 | uint liquidity, 29 | uint amountAMin, 30 | uint amountBMin, 31 | address to, 32 | uint deadline 33 | ) external returns (uint amountA, uint amountB); 34 | function removeLiquidityETH( 35 | address token, 36 | uint liquidity, 37 | uint amountTokenMin, 38 | uint amountETHMin, 39 | address to, 40 | uint deadline 41 | ) external returns (uint amountToken, uint amountETH); 42 | function removeLiquidityWithPermit( 43 | address tokenA, 44 | address tokenB, 45 | uint liquidity, 46 | uint amountAMin, 47 | uint amountBMin, 48 | address to, 49 | uint deadline, 50 | bool approveMax, uint8 v, bytes32 r, bytes32 s 51 | ) external returns (uint amountA, uint amountB); 52 | function removeLiquidityETHWithPermit( 53 | address token, 54 | uint liquidity, 55 | uint amountTokenMin, 56 | uint amountETHMin, 57 | address to, 58 | uint deadline, 59 | bool approveMax, uint8 v, bytes32 r, bytes32 s 60 | ) external returns (uint amountToken, uint amountETH); 61 | function swapExactTokensForTokens( 62 | uint amountIn, 63 | uint amountOutMin, 64 | address[] calldata path, 65 | address to, 66 | uint deadline 67 | ) external returns (uint[] memory amounts); 68 | function swapTokensForExactTokens( 69 | uint amountOut, 70 | uint amountInMax, 71 | address[] calldata path, 72 | address to, 73 | uint deadline 74 | ) external returns (uint[] memory amounts); 75 | function swapExactETHForTokens(uint amountOutMin, address[] calldata path, address to, uint deadline) 76 | external 77 | payable 78 | returns (uint[] memory amounts); 79 | function swapTokensForExactETH(uint amountOut, uint amountInMax, address[] calldata path, address to, uint deadline) 80 | external 81 | returns (uint[] memory amounts); 82 | function swapExactTokensForETH(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline) 83 | external 84 | returns (uint[] memory amounts); 85 | function swapETHForExactTokens(uint amountOut, address[] calldata path, address to, uint deadline) 86 | external 87 | payable 88 | returns (uint[] memory amounts); 89 | 90 | function quote(uint amountA, uint reserveA, uint reserveB) external pure returns (uint amountB); 91 | function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) external pure returns (uint amountOut); 92 | function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut) external pure returns (uint amountIn); 93 | function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts); 94 | function getAmountsIn(uint amountOut, address[] calldata path) external view returns (uint[] memory amounts); 95 | } -------------------------------------------------------------------------------- /contracts/pool/IUniswapV2Router02.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.6.2; 2 | 3 | import "./IUniswapV2Router01.sol"; 4 | 5 | interface IUniswapV2Router02 is IUniswapV2Router01 { 6 | function removeLiquidityETHSupportingFeeOnTransferTokens( 7 | address token, 8 | uint liquidity, 9 | uint amountTokenMin, 10 | uint amountETHMin, 11 | address to, 12 | uint deadline 13 | ) external returns (uint amountETH); 14 | function removeLiquidityETHWithPermitSupportingFeeOnTransferTokens( 15 | address token, 16 | uint liquidity, 17 | uint amountTokenMin, 18 | uint amountETHMin, 19 | address to, 20 | uint deadline, 21 | bool approveMax, uint8 v, bytes32 r, bytes32 s 22 | ) external returns (uint amountETH); 23 | 24 | function swapExactTokensForTokensSupportingFeeOnTransferTokens( 25 | uint amountIn, 26 | uint amountOutMin, 27 | address[] calldata path, 28 | address to, 29 | uint deadline 30 | ) external; 31 | function swapExactETHForTokensSupportingFeeOnTransferTokens( 32 | uint amountOutMin, 33 | address[] calldata path, 34 | address to, 35 | uint deadline 36 | ) external payable; 37 | function swapExactTokensForETHSupportingFeeOnTransferTokens( 38 | uint amountIn, 39 | uint amountOutMin, 40 | address[] calldata path, 41 | address to, 42 | uint deadline 43 | ) external; 44 | } -------------------------------------------------------------------------------- /contracts/tax/BondingTax.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.20; 3 | import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; 4 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 5 | import "@openzeppelin/contracts/utils/math/Math.sol"; 6 | import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; 7 | import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol"; 8 | import "@openzeppelin/contracts/utils/math/Math.sol"; 9 | import "./IBondingTax.sol"; 10 | import "../pool/IRouter.sol"; 11 | 12 | contract BondingTax is 13 | Initializable, 14 | AccessControlUpgradeable, 15 | IBondingTax 16 | { 17 | using SafeERC20 for IERC20; 18 | 19 | bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE"); 20 | 21 | address public assetToken; 22 | address public taxToken; 23 | IRouter public router; 24 | address public bondingRouter; 25 | address public treasury; 26 | uint256 public minSwapThreshold; 27 | uint256 public maxSwapThreshold; 28 | uint16 private _slippage; 29 | 30 | event SwapParamsUpdated( 31 | address oldRouter, 32 | address newRouter, 33 | address oldBondingRouter, 34 | address newBondingRouter, 35 | address oldAsset, 36 | address newAsset 37 | ); 38 | event SwapThresholdUpdated( 39 | uint256 oldMinThreshold, 40 | uint256 newMinThreshold, 41 | uint256 oldMaxThreshold, 42 | uint256 newMaxThreshold 43 | ); 44 | event TreasuryUpdated(address oldTreasury, address newTreasury); 45 | event SwapExecuted(uint256 taxTokenAmount, uint256 assetTokenAmount); 46 | event SwapFailed(uint256 taxTokenAmount); 47 | 48 | /// @custom:oz-upgrades-unsafe-allow constructor 49 | constructor() { 50 | _disableInitializers(); 51 | } 52 | 53 | modifier onlyBondingRouter() { 54 | require(_msgSender() == address(bondingRouter), "Only bonding router"); 55 | _; 56 | } 57 | 58 | function initialize( 59 | address defaultAdmin_, 60 | address assetToken_, 61 | address taxToken_, 62 | address router_, 63 | address bondingRouter_, 64 | address treasury_, 65 | uint256 minSwapThreshold_, 66 | uint256 maxSwapThreshold_ 67 | ) external initializer { 68 | __AccessControl_init(); 69 | 70 | _grantRole(ADMIN_ROLE, defaultAdmin_); 71 | _grantRole(DEFAULT_ADMIN_ROLE, defaultAdmin_); 72 | assetToken = assetToken_; 73 | taxToken = taxToken_; 74 | router = IRouter(router_); 75 | bondingRouter = bondingRouter_; 76 | treasury = treasury_; 77 | minSwapThreshold = minSwapThreshold_; 78 | maxSwapThreshold = maxSwapThreshold_; 79 | IERC20(taxToken).forceApprove(router_, type(uint256).max); 80 | 81 | _slippage = 100; // default to 1% 82 | } 83 | 84 | function updateSwapParams( 85 | address router_, 86 | address bondingRouter_, 87 | address assetToken_, 88 | uint16 slippage_ 89 | ) public onlyRole(ADMIN_ROLE) { 90 | address oldRouter = address(router); 91 | address oldBondingRouter = bondingRouter; 92 | address oldAsset = assetToken; 93 | 94 | assetToken = assetToken_; 95 | router = IRouter(router_); 96 | bondingRouter = bondingRouter_; 97 | _slippage = slippage_; 98 | 99 | IERC20(taxToken).forceApprove(router_, type(uint256).max); 100 | IERC20(taxToken).forceApprove(oldRouter, 0); 101 | 102 | emit SwapParamsUpdated( 103 | oldRouter, 104 | router_, 105 | oldBondingRouter, 106 | bondingRouter_, 107 | oldAsset, 108 | assetToken_ 109 | ); 110 | } 111 | 112 | function updateSwapThresholds( 113 | uint256 minSwapThreshold_, 114 | uint256 maxSwapThreshold_ 115 | ) public onlyRole(ADMIN_ROLE) { 116 | uint256 oldMin = minSwapThreshold; 117 | uint256 oldMax = maxSwapThreshold; 118 | 119 | minSwapThreshold = minSwapThreshold_; 120 | maxSwapThreshold = maxSwapThreshold_; 121 | 122 | emit SwapThresholdUpdated( 123 | oldMin, 124 | minSwapThreshold_, 125 | oldMax, 126 | maxSwapThreshold_ 127 | ); 128 | } 129 | 130 | function updateTreasury(address treasury_) public onlyRole(ADMIN_ROLE) { 131 | address oldTreasury = treasury; 132 | treasury = treasury_; 133 | 134 | emit TreasuryUpdated(oldTreasury, treasury_); 135 | } 136 | 137 | function withdraw(address token) external onlyRole(ADMIN_ROLE) { 138 | IERC20(token).safeTransfer( 139 | treasury, 140 | IERC20(token).balanceOf(address(this)) 141 | ); 142 | } 143 | 144 | function swapForAsset() public onlyBondingRouter returns (bool, uint256) { 145 | uint256 amount = IERC20(taxToken).balanceOf(address(this)); 146 | 147 | if (amount < minSwapThreshold || amount == 0) { 148 | return (false, 0); 149 | } 150 | 151 | if (amount > maxSwapThreshold) { 152 | amount = maxSwapThreshold; 153 | } 154 | 155 | address[] memory path = new address[](2); 156 | path[0] = taxToken; 157 | path[1] = assetToken; 158 | 159 | uint256[] memory amountsOut = router.getAmountsOut(amount, path); 160 | require(amountsOut.length > 1, "Failed to fetch token price"); 161 | 162 | uint256 expectedOutput = amountsOut[1]; 163 | uint256 minOutput = (expectedOutput * (10000 - _slippage)) / 10000; 164 | 165 | try 166 | router.swapExactTokensForTokens( 167 | amount, 168 | minOutput, 169 | path, 170 | treasury, 171 | block.timestamp + 300 172 | ) 173 | returns (uint256[] memory amounts) { 174 | emit SwapExecuted(amount, amounts[1]); 175 | return (true, amounts[1]); 176 | } catch { 177 | emit SwapFailed(amount); 178 | return (false, 0); 179 | } 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /contracts/tax/IBondingTax.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.20; 3 | 4 | interface IBondingTax { 5 | function swapForAsset() external returns (bool, uint256); 6 | } 7 | -------------------------------------------------------------------------------- /contracts/tax/ITBABonus.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.20; 3 | 4 | interface ITBABonus { 5 | function distributeBonus( 6 | uint256 agentId, 7 | address recipient, 8 | uint256 amount 9 | ) external; 10 | } 11 | -------------------------------------------------------------------------------- /contracts/tax/LPRefund.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.20; 3 | import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; 4 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 5 | import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; 6 | import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol"; 7 | 8 | contract LPRefund is Initializable, AccessControlUpgradeable { 9 | using SafeERC20 for IERC20; 10 | 11 | bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE"); 12 | bytes32 public constant EXECUTOR_ROLE = keccak256("EXECUTOR_ROLE"); 13 | 14 | address public taxToken; 15 | 16 | event TaxRefunded( 17 | bytes32 indexed txhash, 18 | address recipient, 19 | uint256 amount 20 | ); 21 | 22 | mapping(bytes32 txhash => uint256 amount) public refunds; 23 | 24 | error TxHashExists(bytes32 txhash); 25 | 26 | /// @custom:oz-upgrades-unsafe-allow constructor 27 | constructor() { 28 | _disableInitializers(); 29 | } 30 | 31 | function initialize( 32 | address defaultAdmin_, 33 | address taxToken_ 34 | ) external initializer { 35 | __AccessControl_init(); 36 | 37 | _grantRole(DEFAULT_ADMIN_ROLE, defaultAdmin_); 38 | _grantRole(ADMIN_ROLE, defaultAdmin_); 39 | 40 | taxToken = taxToken_; 41 | } 42 | 43 | function withdraw(address token) external onlyRole(ADMIN_ROLE) { 44 | IERC20(token).safeTransfer( 45 | _msgSender(), 46 | IERC20(token).balanceOf(address(this)) 47 | ); 48 | } 49 | 50 | function refund( 51 | address recipient, 52 | bytes32[] memory txhashes, 53 | uint256[] memory amounts 54 | ) public onlyRole(EXECUTOR_ROLE) { 55 | require(txhashes.length == amounts.length, "Unmatched inputs"); 56 | uint256 total = 0; 57 | for (uint i = 0; i < txhashes.length; i++) { 58 | bytes32 txhash = txhashes[i]; 59 | uint256 amount = amounts[i]; 60 | 61 | if (refunds[txhash] > 0) { 62 | revert TxHashExists(txhash); 63 | } 64 | refunds[txhash] = amount; 65 | total += amount; 66 | emit TaxRefunded(txhash, recipient, amount); 67 | } 68 | 69 | IERC20(taxToken).safeTransfer(recipient, total); 70 | } 71 | 72 | function manualRefund( 73 | bytes32 txhash, 74 | address recipient, 75 | uint256 amount 76 | ) public onlyRole(ADMIN_ROLE) { 77 | refunds[txhash] += amount; 78 | IERC20(taxToken).safeTransfer(recipient, amount); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /contracts/tax/TBABonus.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.20; 3 | 4 | import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; 5 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 6 | import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; 7 | import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol"; 8 | import "./ITBABonus.sol"; 9 | 10 | contract TBABonus is ITBABonus, Initializable, AccessControlUpgradeable { 11 | using SafeERC20 for IERC20; 12 | 13 | bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE"); 14 | bytes32 public constant EXECUTOR_ROLE = keccak256("EXECUTOR_ROLE"); 15 | 16 | uint256 internal constant DENOM = 10000; 17 | 18 | uint16 public bonusRate; 19 | IERC20 public assetToken; 20 | 21 | mapping(uint256 agentId => uint256 allowance) private _agentAllowances; 22 | mapping(uint256 agentId => uint256 paidAmount) private _agentPaidAmounts; 23 | 24 | event BonusRateUpdated(uint16 oldBonusRate, uint16 newBonusRate); 25 | event AllowanceUpdated(uint256 agentId, uint256 newAllowance); 26 | event PaidAgent(uint256 agentId, uint256 amount); 27 | 28 | /// @custom:oz-upgrades-unsafe-allow constructor 29 | constructor() { 30 | _disableInitializers(); 31 | } 32 | 33 | function initialize( 34 | address defaultAdmin_, 35 | address assetToken_ 36 | ) external initializer { 37 | __AccessControl_init(); 38 | 39 | _grantRole(ADMIN_ROLE, defaultAdmin_); 40 | _grantRole(DEFAULT_ADMIN_ROLE, defaultAdmin_); 41 | 42 | assetToken = IERC20(assetToken_); 43 | 44 | bonusRate = 3500; 45 | } 46 | 47 | function updateBonusRate(uint16 bonusRate_) public onlyRole(ADMIN_ROLE) { 48 | uint16 oldBonusRate = bonusRate; 49 | bonusRate = bonusRate_; 50 | emit BonusRateUpdated(bonusRate_, oldBonusRate); 51 | } 52 | 53 | function setAllowances( 54 | uint256[] memory agentIds, 55 | uint256[] memory allowances 56 | ) public onlyRole(ADMIN_ROLE) { 57 | require(agentIds.length == allowances.length, "Invalid input"); 58 | 59 | for (uint256 i = 0; i < agentIds.length; i++) { 60 | uint256 agentId = agentIds[i]; 61 | uint256 allowance = allowances[i]; 62 | 63 | require( 64 | allowance >= _agentPaidAmounts[agentId], 65 | "Allowance cannot be less than paid amount" 66 | ); 67 | 68 | _agentAllowances[agentId] = allowance; 69 | emit AllowanceUpdated(agentId, allowance); 70 | } 71 | } 72 | 73 | function distributeBonus( 74 | uint256 agentId, 75 | address recipient, 76 | uint256 amount 77 | ) public { 78 | require(agentId > 0, "Invalid agent ID"); 79 | require(recipient != address(0), "Invalid recipient"); 80 | 81 | if (amount == 0 || !hasRole(EXECUTOR_ROLE, msg.sender)) { 82 | return; 83 | } 84 | 85 | uint256 allowance = _agentAllowances[agentId] - 86 | _agentPaidAmounts[agentId]; 87 | uint256 bonus = (amount * bonusRate) / DENOM; 88 | 89 | if (bonus > allowance) { 90 | bonus = allowance; 91 | } 92 | 93 | if (bonus > 0 && assetToken.balanceOf(address(this)) >= bonus) { 94 | _agentPaidAmounts[agentId] += bonus; 95 | assetToken.safeTransfer(recipient, bonus); 96 | emit PaidAgent(agentId, bonus); 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /contracts/tax/TaxSwapper.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.20; 3 | 4 | import "../pool/IUniswapV2Router02.sol"; 5 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 6 | import "@openzeppelin/contracts/access/AccessControl.sol"; 7 | import "../virtualPersona/IAgentToken.sol"; 8 | 9 | contract TaxSwapper is AccessControl { 10 | address public immutable assetToken; 11 | address public taxRecipient; 12 | 13 | address public immutable taxManager; 14 | 15 | IUniswapV2Router02 internal immutable uniswapRouter; 16 | 17 | uint256 public maxSwapAmount; 18 | mapping(address => uint256) public maxAmountByToken; 19 | 20 | event SwapTax(address indexed token, uint256 amount); 21 | 22 | bytes32 public constant OPS_ROLE = keccak256("OPS_ROLE"); 23 | 24 | constructor( 25 | address assetToken_, 26 | address taxRecipient_, 27 | address router_, 28 | address initialOwner_, 29 | uint256 maxSwapAmount_ 30 | ) { 31 | assetToken = assetToken_; 32 | taxRecipient = taxRecipient_; 33 | uniswapRouter = IUniswapV2Router02(router_); 34 | maxSwapAmount = maxSwapAmount_; 35 | _grantRole(DEFAULT_ADMIN_ROLE, initialOwner_); 36 | _grantRole(OPS_ROLE, initialOwner_); 37 | } 38 | 39 | function setMaxSwapAmount( 40 | uint256 maxSwapAmount_ 41 | ) external onlyRole(DEFAULT_ADMIN_ROLE) { 42 | maxSwapAmount = maxSwapAmount_; 43 | } 44 | 45 | function setMaxAmountByToken( 46 | address token, 47 | uint256 maxAmount 48 | ) external onlyRole(DEFAULT_ADMIN_ROLE) { 49 | maxAmountByToken[token] = maxAmount; 50 | } 51 | 52 | function setTaxRecipient( 53 | address taxRecipient_ 54 | ) external onlyRole(DEFAULT_ADMIN_ROLE) { 55 | taxRecipient = taxRecipient_; 56 | } 57 | 58 | function swapTax(address token) external onlyRole(OPS_ROLE) { 59 | IAgentToken(token).distributeTaxTokens(); 60 | _swapTax(token); 61 | } 62 | 63 | function manualSwapTax(address token) external onlyRole(OPS_ROLE) { 64 | _swapTax(token); 65 | } 66 | 67 | function _swapTax(address token) internal { 68 | uint256 maxAmount = maxAmountByToken[token]; 69 | if (maxAmount == 0) { 70 | maxAmount = maxSwapAmount; 71 | } 72 | 73 | uint256 swapBalance = IERC20(token).balanceOf(address(this)); 74 | if (swapBalance > maxAmount) { 75 | swapBalance = maxAmount; 76 | } 77 | 78 | if (swapBalance == 0) { 79 | return; 80 | } 81 | 82 | address[] memory path = new address[](2); 83 | path[0] = token; 84 | path[1] = assetToken; 85 | 86 | IERC20(token).approve(address(uniswapRouter), swapBalance); 87 | 88 | uniswapRouter.swapExactTokensForTokensSupportingFeeOnTransferTokens( 89 | swapBalance, 90 | 0, 91 | path, 92 | taxRecipient, 93 | block.timestamp + 600 94 | ); 95 | 96 | emit SwapTax(token, swapBalance); 97 | } 98 | 99 | function withdraw(address token) external onlyRole(DEFAULT_ADMIN_ROLE) { 100 | IERC20(token).transfer( 101 | taxRecipient, 102 | IERC20(token).balanceOf(address(this)) 103 | ); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /contracts/token/Airdrop.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.20; 2 | 3 | import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; 4 | 5 | contract Airdrop { 6 | 7 | /** 8 | * 9 | * @param _token ERC20 token to airdrop 10 | * @param _recipients list of recipients 11 | * @param _amounts list of amounts to send each recipient 12 | * @param _total total amount to transfer from caller 13 | */ 14 | function airdrop( 15 | IERC20 _token, 16 | address[] calldata _recipients, 17 | uint256[] calldata _amounts, 18 | uint256 _total 19 | ) external { 20 | // Ensure arrays have same length 21 | require(_recipients.length == _amounts.length, "Length mismatch"); 22 | 23 | // Calculate sum of amounts 24 | uint256 sum; 25 | for(uint256 i = 0; i < _amounts.length; i++) { 26 | sum += _amounts[i]; 27 | } 28 | 29 | // Verify sum equals total 30 | require(sum == _total, "Sum != total"); 31 | 32 | // bytes selector for transferFrom(address,address,uint256) 33 | bytes4 transferFrom = 0x23b872dd; 34 | // bytes selector for transfer(address,uint256) 35 | bytes4 transfer = 0xa9059cbb; 36 | 37 | assembly { 38 | // store transferFrom selector 39 | let transferFromData := add(0x20, mload(0x40)) 40 | mstore(transferFromData, transferFrom) 41 | // store caller address 42 | mstore(add(transferFromData, 0x04), caller()) 43 | // store address 44 | mstore(add(transferFromData, 0x24), address()) 45 | // store _total 46 | mstore(add(transferFromData, 0x44), _total) 47 | // call transferFrom for _total 48 | 49 | if iszero( 50 | and( // The arguments of `and` are evaluated from right to left. 51 | or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing. 52 | call(gas(), _token, 0, transferFromData, 0x64, 0x00, 0x20) 53 | ) 54 | ) { 55 | revert(0, 0) 56 | } 57 | 58 | // store transfer selector 59 | let transferData := add(0x20, mload(0x40)) 60 | mstore(transferData, transfer) 61 | 62 | // store length of _recipients 63 | let sz := _amounts.length 64 | 65 | // loop through _recipients 66 | for { 67 | let i := 0 68 | } lt(i, sz) { 69 | // increment i 70 | i := add(i, 1) 71 | } { 72 | // store offset for _amounts[i] 73 | let offset := mul(i, 0x20) 74 | // store _amounts[i] 75 | let amt := calldataload(add(_amounts.offset, offset)) 76 | // store _recipients[i] 77 | let recp := calldataload(add(_recipients.offset, offset)) 78 | // store _recipients[i] in transferData 79 | mstore( 80 | add(transferData, 0x04), 81 | recp 82 | ) 83 | // store _amounts[i] in transferData 84 | mstore( 85 | add(transferData, 0x24), 86 | amt 87 | ) 88 | // call transfer for _amounts[i] to _recipients[i] 89 | // Perform the transfer, reverting upon failure. 90 | if iszero( 91 | and( // The arguments of `and` are evaluated from right to left. 92 | or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing. 93 | call(gas(), _token, 0, transferData, 0x44, 0x00, 0x20) 94 | ) 95 | ) { 96 | revert(0, 0) 97 | } 98 | } 99 | } 100 | 101 | } 102 | } -------------------------------------------------------------------------------- /contracts/token/IMinter.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.20; 3 | 4 | interface IMinter { 5 | function mint(uint256 nftId) external; 6 | 7 | event ImpactMultiplierUpdated(uint256 newMultiplier); 8 | event AgentImpactMultiplierUpdated( 9 | uint256 indexed virtualId, 10 | uint256 newMultiplier 11 | ); 12 | event IPShareUpdated(uint256 newMultiplier); 13 | event AgentIPShareUpdated( 14 | uint256 indexed virtualId, 15 | uint256 newMultiplier 16 | ); 17 | } 18 | -------------------------------------------------------------------------------- /contracts/token/IVEVirtual.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.20; 3 | 4 | interface IVEVirtual { 5 | function balanceOfAt( 6 | address account, 7 | uint256 timestamp 8 | ) external view returns (uint256); 9 | 10 | function balanceOf(address account) external view returns (uint256); 11 | 12 | function balanceOfLock( 13 | address account, 14 | uint256 index 15 | ) external view returns (uint256); 16 | } 17 | -------------------------------------------------------------------------------- /contracts/token/Virtual.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.20; 2 | 3 | // SPDX-License-Identifier: MIT 4 | 5 | import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; 6 | import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Capped.sol"; 7 | import "@openzeppelin/contracts/access/Ownable.sol"; 8 | 9 | contract VirtualToken is ERC20Capped, Ownable { 10 | constructor(uint256 _initialSupply, address initialOwner) ERC20("Virtual Protocol", "VIRTUAL") ERC20Capped(1000000000*10**18) Ownable(initialOwner){ 11 | ERC20._mint(msg.sender, _initialSupply); 12 | } 13 | 14 | function mint(address _to, uint256 _amount) onlyOwner external { 15 | _mint(_to, _amount); 16 | } 17 | } -------------------------------------------------------------------------------- /contracts/token/veViewer.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.20; 3 | 4 | import "../token/veVirtual.sol"; 5 | 6 | contract veViewer { 7 | veVirtual token; 8 | 9 | constructor(address token_) { 10 | token = veVirtual(token_); 11 | } 12 | 13 | function balanceOfAt( 14 | address[] memory accounts, 15 | uint256 ts 16 | ) external view returns (uint256) { 17 | uint256 total = 0; 18 | for (uint256 i = 0; i < accounts.length; i++) { 19 | total += token.balanceOfAt(accounts[i], ts); 20 | } 21 | return total; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /contracts/virtualPersona/AgentVeToken.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.20; 3 | 4 | import "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol"; 5 | import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; 6 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 7 | import {Checkpoints} from "@openzeppelin/contracts/utils/structs/Checkpoints.sol"; 8 | import "./IAgentVeToken.sol"; 9 | import "./IAgentNft.sol"; 10 | import "./ERC20Votes.sol"; 11 | import "@openzeppelin/contracts/access/IAccessControl.sol"; 12 | 13 | contract AgentVeToken is IAgentVeToken, ERC20Upgradeable, ERC20Votes { 14 | using SafeERC20 for IERC20; 15 | using Checkpoints for Checkpoints.Trace208; 16 | 17 | address public founder; 18 | address public assetToken; // This is the token that is staked 19 | address public agentNft; 20 | uint256 public matureAt; // The timestamp when the founder can withdraw the tokens 21 | bool public canStake; // To control private/public agent mode 22 | uint256 public initialLock; // Initial locked amount 23 | 24 | constructor() { 25 | _disableInitializers(); 26 | } 27 | 28 | mapping(address => Checkpoints.Trace208) private _balanceCheckpoints; 29 | 30 | bool internal locked; 31 | 32 | modifier noReentrant() { 33 | require(!locked, "cannot reenter"); 34 | locked = true; 35 | _; 36 | locked = false; 37 | } 38 | 39 | function initialize( 40 | string memory _name, 41 | string memory _symbol, 42 | address _founder, 43 | address _assetToken, 44 | uint256 _matureAt, 45 | address _agentNft, 46 | bool _canStake 47 | ) external initializer { 48 | __ERC20_init(_name, _symbol); 49 | __ERC20Votes_init(); 50 | 51 | founder = _founder; 52 | matureAt = _matureAt; 53 | assetToken = _assetToken; 54 | agentNft = _agentNft; 55 | canStake = _canStake; 56 | } 57 | 58 | // Stakers have to stake their tokens and delegate to a validator 59 | function stake(uint256 amount, address receiver, address delegatee) public { 60 | require( 61 | canStake || totalSupply() == 0, 62 | "Staking is disabled for private agent" 63 | ); // Either public or first staker 64 | 65 | address sender = _msgSender(); 66 | require(amount > 0, "Cannot stake 0"); 67 | require( 68 | IERC20(assetToken).balanceOf(sender) >= amount, 69 | "Insufficient asset token balance" 70 | ); 71 | require( 72 | IERC20(assetToken).allowance(sender, address(this)) >= amount, 73 | "Insufficient asset token allowance" 74 | ); 75 | 76 | IAgentNft registry = IAgentNft(agentNft); 77 | uint256 virtualId = registry.stakingTokenToVirtualId(address(this)); 78 | 79 | require(!registry.isBlacklisted(virtualId), "Agent Blacklisted"); 80 | 81 | if (totalSupply() == 0) { 82 | initialLock = amount; 83 | } 84 | 85 | registry.addValidator(virtualId, delegatee); 86 | 87 | IERC20(assetToken).safeTransferFrom(sender, address(this), amount); 88 | _mint(receiver, amount); 89 | _delegate(receiver, delegatee); 90 | _balanceCheckpoints[receiver].push( 91 | clock(), 92 | SafeCast.toUint208(balanceOf(receiver)) 93 | ); 94 | } 95 | 96 | function setCanStake(bool _canStake) public { 97 | require(_msgSender() == founder, "Not founder"); 98 | canStake = _canStake; 99 | } 100 | 101 | function setMatureAt(uint256 _matureAt) public { 102 | bytes32 ADMIN_ROLE = keccak256("ADMIN_ROLE"); 103 | require( 104 | IAccessControl(agentNft).hasRole(ADMIN_ROLE, _msgSender()), 105 | "Not admin" 106 | ); 107 | matureAt = _matureAt; 108 | } 109 | 110 | function withdraw(uint256 amount) public noReentrant { 111 | address sender = _msgSender(); 112 | require(balanceOf(sender) >= amount, "Insufficient balance"); 113 | 114 | if ( 115 | (sender == founder) && ((balanceOf(sender) - amount) < initialLock) 116 | ) { 117 | require(block.timestamp >= matureAt, "Not mature yet"); 118 | } 119 | 120 | _burn(sender, amount); 121 | _balanceCheckpoints[sender].push( 122 | clock(), 123 | SafeCast.toUint208(balanceOf(sender)) 124 | ); 125 | 126 | IERC20(assetToken).safeTransfer(sender, amount); 127 | } 128 | 129 | function getPastBalanceOf( 130 | address account, 131 | uint256 timepoint 132 | ) public view returns (uint256) { 133 | uint48 currentTimepoint = clock(); 134 | if (timepoint >= currentTimepoint) { 135 | revert ERC5805FutureLookup(timepoint, currentTimepoint); 136 | } 137 | return 138 | _balanceCheckpoints[account].upperLookupRecent( 139 | SafeCast.toUint48(timepoint) 140 | ); 141 | } 142 | 143 | // This is non-transferable token 144 | function transfer( 145 | address /*to*/, 146 | uint256 /*value*/ 147 | ) public override returns (bool) { 148 | revert("Transfer not supported"); 149 | } 150 | 151 | function transferFrom( 152 | address /*from*/, 153 | address /*to*/, 154 | uint256 /*value*/ 155 | ) public override returns (bool) { 156 | revert("Transfer not supported"); 157 | } 158 | 159 | function approve( 160 | address /*spender*/, 161 | uint256 /*value*/ 162 | ) public override returns (bool) { 163 | revert("Approve not supported"); 164 | } 165 | 166 | // The following functions are overrides required by Solidity. 167 | function _update( 168 | address from, 169 | address to, 170 | uint256 value 171 | ) internal override(ERC20Upgradeable, ERC20VotesUpgradeable) { 172 | super._update(from, to, value); 173 | } 174 | 175 | function getPastDelegates( 176 | address account, 177 | uint256 timepoint 178 | ) public view returns (address) { 179 | return super._getPastDelegates(account, timepoint); 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /contracts/virtualPersona/CoreRegistry.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.20; 3 | 4 | import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; 5 | 6 | abstract contract CoreRegistry is Initializable { 7 | mapping(uint8 => string) public coreTypes; 8 | uint8 _nextCoreType; 9 | 10 | event NewCoreType(uint8 coreType, string label); 11 | 12 | function __CoreRegistry_init() internal onlyInitializing { 13 | coreTypes[_nextCoreType++] = "Cognitive Core"; 14 | coreTypes[_nextCoreType++] = "Voice and Sound Core"; 15 | coreTypes[_nextCoreType++] = "Visual Core"; 16 | coreTypes[_nextCoreType++] = "Domain Expertise Core"; 17 | } 18 | 19 | function _addCoreType(string memory label) internal { 20 | uint8 coreType = _nextCoreType++; 21 | coreTypes[coreType] = label; 22 | emit NewCoreType(coreType, label); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /contracts/virtualPersona/ERC20Votes.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.20; 3 | 4 | // This contract extends ERC20VotesUpgradeable to add tracking of delegatee changes 5 | import "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20VotesUpgradeable.sol"; 6 | import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol"; 7 | import "../libs/AddressCheckpoints.sol"; 8 | 9 | abstract contract ERC20Votes is ERC20VotesUpgradeable { 10 | using AddressCheckpoints for AddressCheckpoints.Trace; 11 | mapping(address => AddressCheckpoints.Trace) private _delegateeCheckpoints; 12 | 13 | function _delegate(address account, address delegatee) internal override { 14 | super._delegate(account, delegatee); 15 | _delegateeCheckpoints[account].push(clock(), delegatee); 16 | } 17 | 18 | function _getPastDelegates( 19 | address account, 20 | uint256 timepoint 21 | ) internal view virtual returns (address) { 22 | uint48 currentTimepoint = clock(); 23 | if (timepoint >= currentTimepoint) { 24 | revert ERC5805FutureLookup(timepoint, currentTimepoint); 25 | } 26 | return 27 | _delegateeCheckpoints[account].upperLookupRecent( 28 | SafeCast.toUint48(timepoint) 29 | ); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /contracts/virtualPersona/EloCalculator.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.20; 3 | 4 | import "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol"; 5 | import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; 6 | import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; 7 | import {Elo} from "../libs/Elo.sol"; 8 | import "./IEloCalculator.sol"; 9 | 10 | contract EloCalculator is IEloCalculator, Initializable, OwnableUpgradeable { 11 | uint256 public k; 12 | 13 | /// @custom:oz-upgrades-unsafe-allow constructor 14 | constructor() { 15 | _disableInitializers(); 16 | } 17 | 18 | function initialize(address initialOwner) public initializer { 19 | __Ownable_init(initialOwner); 20 | k = 30; 21 | } 22 | 23 | function mapBattleResultToGameResult( 24 | uint8 result 25 | ) internal pure returns (uint256) { 26 | if (result == 1) { 27 | return 100; 28 | } else if (result == 2) { 29 | return 50; 30 | } else if (result == 3) { 31 | return 50; 32 | } 33 | return 0; 34 | } 35 | 36 | function _roundUp( 37 | uint256 numerator, 38 | uint256 denominator 39 | ) internal pure returns (uint256) { 40 | return (numerator + denominator - 1) / denominator; 41 | } 42 | 43 | // Get winner elo rating 44 | function battleElo( 45 | uint256 currentRating, 46 | uint8[] memory battles 47 | ) public view returns (uint256) { 48 | uint256 eloA = 1000; 49 | uint256 eloB = 1000; 50 | for (uint256 i = 0; i < battles.length; i++) { 51 | uint256 result = mapBattleResultToGameResult(battles[i]); 52 | (uint256 change, bool negative) = Elo.ratingChange( 53 | eloB, 54 | eloA, 55 | result, 56 | k 57 | ); 58 | change = _roundUp(change, 100); 59 | if (negative) { 60 | eloA -= change; 61 | eloB += change; 62 | } else { 63 | eloA += change; 64 | eloB -= change; 65 | } 66 | } 67 | return currentRating + eloA - 1000; 68 | } 69 | 70 | function setK(uint256 k_) public onlyOwner { 71 | k = k_; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /contracts/virtualPersona/GovernorCountingSimpleUpgradeable.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // OpenZeppelin Contracts (last updated v5.0.0) (governance/extensions/GovernorCountingSimple.sol) 3 | 4 | pragma solidity ^0.8.20; 5 | 6 | import "@openzeppelin/contracts-upgradeable/governance/GovernorUpgradeable.sol"; 7 | 8 | /** 9 | * @dev Extension of {Governor} for simple, 4 options, vote counting. 10 | */ 11 | abstract contract GovernorCountingSimpleUpgradeable is GovernorUpgradeable { 12 | /** 13 | * @dev Supported vote types. Matches Governor Bravo ordering. 14 | */ 15 | enum VoteType { 16 | Against, 17 | For, 18 | Abstain, 19 | Deliberate 20 | } 21 | 22 | struct ProposalVote { 23 | uint256 againstVotes; 24 | uint256 forVotes; 25 | uint256 abstainVotes; 26 | mapping(address voter => bool) hasVoted; 27 | } 28 | 29 | mapping(uint256 proposalId => ProposalVote) private _proposalVotes; 30 | 31 | function __GovernorCountingSimple_init() internal onlyInitializing {} 32 | 33 | /** 34 | * @dev See {IGovernor-COUNTING_MODE}. 35 | */ 36 | // solhint-disable-next-line func-name-mixedcase 37 | function COUNTING_MODE() 38 | public 39 | pure 40 | virtual 41 | override 42 | returns (string memory) 43 | { 44 | return "support=bravo&quorum=for,abstain"; 45 | } 46 | 47 | /** 48 | * @dev See {IGovernor-hasVoted}. 49 | */ 50 | function hasVoted( 51 | uint256 proposalId, 52 | address account 53 | ) public view virtual override returns (bool) { 54 | return _proposalVotes[proposalId].hasVoted[account]; 55 | } 56 | 57 | /** 58 | * @dev Accessor to the internal vote counts. 59 | */ 60 | function proposalVotes( 61 | uint256 proposalId 62 | ) 63 | public 64 | view 65 | virtual 66 | returns (uint256 againstVotes, uint256 forVotes, uint256 abstainVotes) 67 | { 68 | ProposalVote storage proposalVote = _proposalVotes[proposalId]; 69 | return ( 70 | proposalVote.againstVotes, 71 | proposalVote.forVotes, 72 | proposalVote.abstainVotes 73 | ); 74 | } 75 | 76 | /** 77 | * @dev See {Governor-_quorumReached}. 78 | */ 79 | function _quorumReached( 80 | uint256 proposalId 81 | ) internal view virtual override returns (bool) { 82 | ProposalVote storage proposalVote = _proposalVotes[proposalId]; 83 | 84 | return 85 | quorum(proposalSnapshot(proposalId)) <= 86 | proposalVote.forVotes + proposalVote.abstainVotes; 87 | } 88 | 89 | /** 90 | * @dev See {Governor-_voteSucceeded}. In this module, the forVotes must be strictly over the againstVotes. 91 | */ 92 | function _voteSucceeded( 93 | uint256 proposalId 94 | ) internal view virtual override returns (bool) { 95 | ProposalVote storage proposalVote = _proposalVotes[proposalId]; 96 | 97 | return proposalVote.forVotes > proposalVote.againstVotes; 98 | } 99 | 100 | /** 101 | * @dev See {Governor-_countVote}. In this module, the support follows the `VoteType` enum (from Governor Bravo). 102 | */ 103 | function _countVote( 104 | uint256 proposalId, 105 | address account, 106 | uint8 support, 107 | uint256 weight, 108 | bytes memory // params 109 | ) internal virtual override { 110 | ProposalVote storage proposalVote = _proposalVotes[proposalId]; 111 | 112 | // Allow user to cast vote with opinion and type "Deliberate" to signal that they are not voting 113 | if (support == uint8(VoteType.Deliberate)) { 114 | return; 115 | } 116 | 117 | if (proposalVote.hasVoted[account]) { 118 | revert GovernorAlreadyCastVote(account); 119 | } 120 | 121 | proposalVote.hasVoted[account] = true; 122 | 123 | if (support == uint8(VoteType.Against)) { 124 | proposalVote.againstVotes += weight; 125 | } else if (support == uint8(VoteType.For)) { 126 | proposalVote.forVotes += weight; 127 | } else if (support == uint8(VoteType.Abstain)) { 128 | proposalVote.abstainVotes += weight; 129 | } else { 130 | revert GovernorInvalidVoteType(); 131 | } 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /contracts/virtualPersona/IAgentDAO.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.20; 3 | 4 | import "@openzeppelin/contracts/governance/IGovernor.sol"; 5 | import "@openzeppelin/contracts/governance/utils/IVotes.sol"; 6 | 7 | interface IAgentDAO { 8 | function initialize( 9 | string memory name, 10 | IVotes token, 11 | address agentNft, 12 | uint256 threshold, 13 | uint32 votingPeriod_ 14 | ) external; 15 | 16 | function proposalCount() external view returns (uint256); 17 | 18 | function scoreOf(address account) external view returns (uint256); 19 | 20 | function totalScore() external view returns (uint256); 21 | 22 | function getPastScore( 23 | address account, 24 | uint256 timepoint 25 | ) external view returns (uint256); 26 | 27 | function getMaturity(uint256 proposalId) external view returns (uint256); 28 | 29 | event ValidatorEloRating(uint256 proposalId, address voter, uint256 score, uint8[] votes); 30 | } 31 | -------------------------------------------------------------------------------- /contracts/virtualPersona/IAgentFactory.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.20; 3 | 4 | import "@openzeppelin/contracts/governance/IGovernor.sol"; 5 | 6 | interface IAgentFactory { 7 | function proposeAgent( 8 | string memory name, 9 | string memory symbol, 10 | string memory tokenURI, 11 | uint8[] memory cores, 12 | bytes32 tbaSalt, 13 | address tbaImplementation, 14 | uint32 daoVotingPeriod, 15 | uint256 daoThreshold 16 | ) external returns (uint256); 17 | 18 | function withdraw(uint256 id) external; 19 | 20 | function totalAgents() external view returns (uint256); 21 | } 22 | -------------------------------------------------------------------------------- /contracts/virtualPersona/IAgentFactoryV3.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.20; 3 | 4 | import "@openzeppelin/contracts/governance/IGovernor.sol"; 5 | 6 | interface IAgentFactoryV3 { 7 | function proposeAgent( 8 | string memory name, 9 | string memory symbol, 10 | string memory tokenURI, 11 | uint8[] memory cores, 12 | bytes32 tbaSalt, 13 | address tbaImplementation, 14 | uint32 daoVotingPeriod, 15 | uint256 daoThreshold 16 | ) external returns (uint256); 17 | 18 | function withdraw(uint256 id) external; 19 | 20 | function totalAgents() external view returns (uint256); 21 | 22 | function initFromBondingCurve( 23 | string memory name, 24 | string memory symbol, 25 | uint8[] memory cores, 26 | bytes32 tbaSalt, 27 | address tbaImplementation, 28 | uint32 daoVotingPeriod, 29 | uint256 daoThreshold, 30 | uint256 applicationThreshold_, 31 | address creator 32 | ) external returns (uint256); 33 | 34 | function executeBondingCurveApplication( 35 | uint256 id, 36 | uint256 totalSupply, 37 | uint256 lpSupply, 38 | address vault 39 | ) external returns (address); 40 | 41 | function executeBondingCurveApplicationSalt( 42 | uint256 id, 43 | uint256 totalSupply, 44 | uint256 lpSupply, 45 | address vault, 46 | bytes32 salt 47 | ) external returns (address); 48 | } 49 | -------------------------------------------------------------------------------- /contracts/virtualPersona/IAgentFactoryV4.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.20; 3 | 4 | import "@openzeppelin/contracts/governance/IGovernor.sol"; 5 | 6 | interface IAgentFactoryV4 { 7 | function proposeAgent( 8 | string memory name, 9 | string memory symbol, 10 | string memory tokenURI, 11 | uint8[] memory cores, 12 | bytes32 tbaSalt, 13 | address tbaImplementation, 14 | uint32 daoVotingPeriod, 15 | uint256 daoThreshold 16 | ) external returns (uint256); 17 | 18 | function withdraw(uint256 id) external; 19 | 20 | function totalAgents() external view returns (uint256); 21 | 22 | function initFromToken( 23 | address tokenAddr, 24 | uint8[] memory cores, 25 | bytes32 tbaSalt, 26 | address tbaImplementation, 27 | uint32 daoVotingPeriod, 28 | uint256 daoThreshold, 29 | uint256 initialLP 30 | ) external returns (uint256); 31 | 32 | function executeTokenApplication( 33 | uint256 id, 34 | bool canStake 35 | ) external; 36 | } 37 | -------------------------------------------------------------------------------- /contracts/virtualPersona/IAgentNft.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.20; 3 | 4 | import "./IValidatorRegistry.sol"; 5 | 6 | interface IAgentNft is IValidatorRegistry { 7 | struct VirtualInfo { 8 | address dao; // Agent DAO can update the agent metadata 9 | address token; 10 | address founder; 11 | address tba; // Token Bound Address 12 | uint8[] coreTypes; 13 | } 14 | 15 | event CoresUpdated(uint256 virtualId, uint8[] coreTypes); 16 | 17 | struct VirtualLP { 18 | address pool; // Liquidity pool for the agent 19 | address veToken; // Voting escrow token 20 | } 21 | 22 | function mint( 23 | uint256 id, 24 | address to, 25 | string memory newTokenURI, 26 | address payable theDAO, 27 | address founder, 28 | uint8[] memory coreTypes, 29 | address pool, 30 | address token 31 | ) external returns (uint256); 32 | 33 | function stakingTokenToVirtualId( 34 | address daoToken 35 | ) external view returns (uint256); 36 | 37 | function setTBA(uint256 virtualId, address tba) external; 38 | 39 | function virtualInfo( 40 | uint256 virtualId 41 | ) external view returns (VirtualInfo memory); 42 | 43 | function virtualLP( 44 | uint256 virtualId 45 | ) external view returns (VirtualLP memory); 46 | 47 | function totalSupply() external view returns (uint256); 48 | 49 | function totalStaked(uint256 virtualId) external view returns (uint256); 50 | 51 | function getVotes( 52 | uint256 virtualId, 53 | address validator 54 | ) external view returns (uint256); 55 | 56 | function totalProposals(uint256 virtualId) external view returns (uint256); 57 | 58 | function getContributionNft() external view returns (address); 59 | 60 | function getServiceNft() external view returns (address); 61 | 62 | function getAllServices( 63 | uint256 virtualId 64 | ) external view returns (uint256[] memory); 65 | 66 | function nextVirtualId() external view returns (uint256); 67 | 68 | function isBlacklisted(uint256 virtualId) external view returns (bool); 69 | 70 | function getEloCalculator() external view returns (address); 71 | } 72 | -------------------------------------------------------------------------------- /contracts/virtualPersona/IAgentVeToken.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.20; 3 | 4 | interface IAgentVeToken { 5 | function initialize( 6 | string memory _name, 7 | string memory _symbol, 8 | address _founder, 9 | address _assetToken, 10 | uint256 _matureAt, 11 | address _agentNft, 12 | bool _canStake 13 | ) external; 14 | 15 | function stake( 16 | uint256 amount, 17 | address receiver, 18 | address delegatee 19 | ) external; 20 | 21 | function withdraw(uint256 amount) external; 22 | 23 | function getPastDelegates( 24 | address account, 25 | uint256 timepoint 26 | ) external view returns (address); 27 | 28 | function getPastBalanceOf( 29 | address account, 30 | uint256 timepoint 31 | ) external view returns (uint256); 32 | } 33 | -------------------------------------------------------------------------------- /contracts/virtualPersona/IERC20Config.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.20; 3 | 4 | interface IERC20Config { 5 | struct ERC20Config { 6 | bytes baseParameters; 7 | bytes supplyParameters; 8 | bytes taxParameters; 9 | bytes poolParameters; 10 | } 11 | 12 | struct ERC20BaseParameters { 13 | string name; 14 | string symbol; 15 | } 16 | 17 | struct ERC20SupplyParameters { 18 | uint256 maxSupply; 19 | uint256 lpSupply; 20 | uint256 vaultSupply; 21 | uint256 maxTokensPerWallet; 22 | uint256 maxTokensPerTxn; 23 | uint256 botProtectionDurationInSeconds; 24 | address vault; 25 | } 26 | 27 | struct ERC20TaxParameters { 28 | uint256 projectBuyTaxBasisPoints; 29 | uint256 projectSellTaxBasisPoints; 30 | uint256 taxSwapThresholdBasisPoints; 31 | address projectTaxRecipient; 32 | } 33 | } -------------------------------------------------------------------------------- /contracts/virtualPersona/IEloCalculator.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.20; 3 | 4 | interface IEloCalculator { 5 | function battleElo( 6 | uint256 currentRating, 7 | uint8[] memory battles 8 | ) external view returns (uint256); 9 | } 10 | -------------------------------------------------------------------------------- /contracts/virtualPersona/IExecutionInterface.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.20; 3 | 4 | interface IExecutionInterface { 5 | function execute(address to, uint256 value, bytes memory data, uint8 operation) external returns (bool success); 6 | } -------------------------------------------------------------------------------- /contracts/virtualPersona/IValidatorRegistry.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.20; 3 | 4 | interface IValidatorRegistry { 5 | event NewValidator(uint256 virtualId, address account); 6 | 7 | function isValidator( 8 | uint256 virtualId, 9 | address account 10 | ) external view returns (bool); 11 | 12 | function validatorScore( 13 | uint256 virtualId, 14 | address validator 15 | ) external view returns (uint256); 16 | 17 | function getPastValidatorScore( 18 | uint256 virtualId, 19 | address validator, 20 | uint256 timepoint 21 | ) external view returns (uint256); 22 | 23 | function validatorCount(uint256 virtualId) external view returns (uint256); 24 | 25 | function validatorAt( 26 | uint256 virtualId, 27 | uint256 index 28 | ) external view returns (address); 29 | 30 | function totalUptimeScore( 31 | uint256 virtualId 32 | ) external view returns (uint256); 33 | 34 | function addValidator(uint256 virtualId, address validator) external; 35 | } 36 | -------------------------------------------------------------------------------- /contracts/virtualPersona/ValidatorRegistry.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.20; 3 | 4 | import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; 5 | import "./IValidatorRegistry.sol"; 6 | 7 | abstract contract ValidatorRegistry is IValidatorRegistry, Initializable { 8 | mapping(uint256 virtualId => mapping(address account => bool isValidator)) 9 | private _validatorsMap; 10 | mapping(address account => mapping(uint256 virtualId => uint256 score)) 11 | private _baseValidatorScore; 12 | mapping(uint256 virtualId => address[] validators) private _validators; 13 | 14 | function(uint256, address) view returns (uint256) private _getScoreOf; 15 | function(uint256) view returns (uint256) private _getMaxScore; 16 | function(uint256, address, uint256) view returns (uint256) 17 | private _getPastScore; 18 | 19 | function __ValidatorRegistry_init( 20 | function(uint256, address) view returns (uint256) getScoreOf_, 21 | function(uint256) view returns (uint256) getMaxScore_, 22 | function(uint256, address, uint256) view returns (uint256) getPastScore_ 23 | ) internal onlyInitializing { 24 | _getScoreOf = getScoreOf_; 25 | _getMaxScore = getMaxScore_; 26 | _getPastScore = getPastScore_; 27 | } 28 | 29 | function isValidator( 30 | uint256 virtualId, 31 | address account 32 | ) public view returns (bool) { 33 | return _validatorsMap[virtualId][account]; 34 | } 35 | 36 | function _addValidator(uint256 virtualId, address validator) internal { 37 | _validatorsMap[virtualId][validator] = true; 38 | _validators[virtualId].push(validator); 39 | emit NewValidator(virtualId, validator); 40 | } 41 | 42 | function _initValidatorScore( 43 | uint256 virtualId, 44 | address validator 45 | ) internal { 46 | _baseValidatorScore[validator][virtualId] = _getMaxScore(virtualId); 47 | } 48 | 49 | function validatorScore( 50 | uint256 virtualId, 51 | address validator 52 | ) public view virtual returns (uint256) { 53 | return 54 | _baseValidatorScore[validator][virtualId] + 55 | _getScoreOf(virtualId, validator); 56 | } 57 | 58 | function getPastValidatorScore( 59 | uint256 virtualId, 60 | address validator, 61 | uint256 timepoint 62 | ) public view virtual returns (uint256) { 63 | return 64 | _baseValidatorScore[validator][virtualId] + 65 | _getPastScore(virtualId, validator, timepoint); 66 | } 67 | 68 | function validatorCount(uint256 virtualId) public view returns (uint256) { 69 | return _validators[virtualId].length; 70 | } 71 | 72 | function validatorAt( 73 | uint256 virtualId, 74 | uint256 index 75 | ) public view returns (address) { 76 | return _validators[virtualId][index]; 77 | } 78 | 79 | function totalUptimeScore(uint256 virtualId) public view returns (uint256) { 80 | uint256 totalScore = 0; 81 | for (uint256 i = 0; i < validatorCount(virtualId); i++) { 82 | totalScore += validatorScore(virtualId, validatorAt(virtualId, i)); 83 | } 84 | return totalScore; 85 | } 86 | 87 | function _migrateScoreFunctions( 88 | function(uint256, address) view returns (uint256) getScoreOf_, 89 | function(uint256) view returns (uint256) getMaxScore_, 90 | function(uint256, address, uint256) view returns (uint256) getPastScore_ 91 | ) internal { 92 | _getScoreOf = getScoreOf_; 93 | _getMaxScore = getMaxScore_; 94 | _getPastScore = getPastScore_; 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /funding.json: -------------------------------------------------------------------------------- 1 | { 2 | "opRetro": { 3 | "projectId": "0x780cf3d5aa4e3c94b8a4157f4b06a3f92ebcc20813585e20e3fe0271bf76b7a9" 4 | } 5 | } -------------------------------------------------------------------------------- /hardhat.config.js: -------------------------------------------------------------------------------- 1 | /** @type import('hardhat/config').HardhatUserConfig */ 2 | require("dotenv").config(); 3 | require("@nomicfoundation/hardhat-toolbox"); 4 | require("hardhat-deploy"); 5 | require("@openzeppelin/hardhat-upgrades"); 6 | require("@fireblocks/hardhat-fireblocks"); 7 | require("hardhat-contract-sizer"); 8 | 9 | const { ApiBaseUrl } = require("@fireblocks/fireblocks-web3-provider"); 10 | 11 | module.exports = { 12 | solidity: { 13 | version: "0.8.26", 14 | settings: { 15 | viaIR: true, 16 | optimizer: { 17 | enabled: true, 18 | runs: 200, 19 | }, 20 | }, 21 | }, 22 | overrides: { 23 | "contracts/genesis/FGenesis.sol": { 24 | version: "0.8.26", 25 | settings: { 26 | optimizer: { 27 | enabled: true, 28 | runs: 200, 29 | }, 30 | viaIR: false, 31 | }, 32 | }, 33 | "contracts/genesis/Genesis.sol": { 34 | version: "0.8.26", 35 | settings: { 36 | optimizer: { 37 | enabled: true, 38 | runs: 200, 39 | }, 40 | viaIR: false, 41 | }, 42 | }, 43 | }, 44 | namedAccounts: { 45 | deployer: `privatekey://${process.env.PRIVATE_KEY}`, 46 | }, 47 | etherscan: { 48 | apiKey: process.env.ETHERSCAN_API_KEY, 49 | customChains: [ 50 | { 51 | network: "base_sepolia", 52 | chainId: 84532, 53 | urls: { 54 | apiURL: "https://api-sepolia.basescan.org/api", 55 | browserURL: "https://sepolia.basescan.org/", 56 | }, 57 | }, 58 | ], 59 | }, 60 | networks: { 61 | base: { 62 | url: "https://mainnet.base.org", 63 | accounts: [process.env.PRIVATE_KEY], 64 | }, 65 | base_fire: { 66 | url: "https://mainnet.base.org", 67 | accounts: [process.env.PRIVATE_KEY], 68 | fireblocks: { 69 | privateKey: process.env.FIREBLOCKS_API_PRIVATE_KEY_PATH, 70 | apiKey: process.env.FIREBLOCKS_API_KEY, 71 | vaultAccountIds: process.env.FIREBLOCKS_VAULT_ACCOUNT_IDS, 72 | }, 73 | }, 74 | base_sepolia: { 75 | url: "https://sepolia.base.org", 76 | accounts: [process.env.PRIVATE_KEY], 77 | verify: { 78 | etherscan: { 79 | apiUrl: "https://api-sepolia.basescan.org", 80 | apiKey: process.env.ETHERSCAN_API_KEY, 81 | }, 82 | }, 83 | }, 84 | base_sepolia_fire: { 85 | url: "https://sepolia.base.org", 86 | accounts: [process.env.PRIVATE_KEY], 87 | verify: { 88 | etherscan: { 89 | apiUrl: "https://api-sepolia.basescan.org", 90 | apiKey: process.env.ETHERSCAN_API_KEY, 91 | }, 92 | }, 93 | fireblocks: { 94 | apiBaseUrl: ApiBaseUrl.Sandbox, 95 | privateKey: process.env.FIREBLOCKS_API_PRIVATE_KEY_PATH, 96 | apiKey: process.env.FIREBLOCKS_API_KEY, 97 | vaultAccountIds: process.env.FIREBLOCKS_VAULT_ACCOUNT_IDS, 98 | }, 99 | }, 100 | local: { 101 | url: "http://127.0.0.1:8545", 102 | }, 103 | polygon: { 104 | url: "https://rpc-mainnet.maticvigil.com/", 105 | accounts: [process.env.PRIVATE_KEY], 106 | }, 107 | mumbai: { 108 | url: "https://rpc.ankr.com/polygon_mumbai", 109 | accounts: [process.env.PRIVATE_KEY], 110 | }, 111 | goerli: { 112 | url: "https://rpc.ankr.com/eth_goerli", 113 | accounts: [process.env.PRIVATE_KEY], 114 | }, 115 | }, 116 | mocha: { 117 | timeout: 100000000, 118 | }, 119 | }; 120 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "virtual-contracts", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "scripts": { 6 | "test": "echo \"Error: no test specified\" && exit 1" 7 | }, 8 | "repository": { 9 | "type": "git", 10 | "url": "git+https://github.com/PathDAO/virtual-contracts.git" 11 | }, 12 | "keywords": [], 13 | "author": "", 14 | "license": "ISC", 15 | "bugs": { 16 | "url": "https://github.com/PathDAO/virtual-contracts/issues" 17 | }, 18 | "homepage": "https://github.com/PathDAO/virtual-contracts#readme", 19 | "description": "", 20 | "devDependencies": { 21 | "@nomicfoundation/hardhat-chai-matchers": "^2.0.0", 22 | "@nomicfoundation/hardhat-ethers": "^3.0.0", 23 | "@nomicfoundation/hardhat-network-helpers": "^1.0.0", 24 | "@nomicfoundation/hardhat-toolbox": "^3.0.0", 25 | "@nomiclabs/hardhat-ethers": "npm:hardhat-deploy-ethers", 26 | "@nomiclabs/hardhat-etherscan": "^3.1.8", 27 | "@types/chai": "^4.2.0", 28 | "@types/mocha": ">=9.1.0", 29 | "ethers": "^6.9.2", 30 | "hardhat": "2.23.0", 31 | "hardhat-contract-sizer": "^2.10.0", 32 | "hardhat-deploy": "^0.11.45", 33 | "hardhat-gas-reporter": "^1.0.8", 34 | "solidity-coverage": "^0.8.1", 35 | "ts-node": "^10.9.2", 36 | "typechain": "^8.2.0", 37 | "typescript": "^5.3.3" 38 | }, 39 | "dependencies": { 40 | "@account-abstraction/contracts": "^0.6.0", 41 | "@fireblocks/hardhat-fireblocks": "^1.3.5", 42 | "@nomicfoundation/hardhat-verify": "^2.0.3", 43 | "@openzeppelin/contracts": "5.0.0", 44 | "@openzeppelin/contracts-upgradeable": "^5.0.1", 45 | "@openzeppelin/hardhat-upgrades": "^3.0.2", 46 | "@typechain/ethers-v6": "^0.5.1", 47 | "@typechain/hardhat": "^9.1.0", 48 | "@uniswap/v2-core": "^1.0.1", 49 | "chai": "^4.2.0", 50 | "dotenv": "^16.3.1", 51 | "erc6551": "^0.3.1" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /scripts/arguments/acpArguments.js: -------------------------------------------------------------------------------- 1 | module.exports = [ 2 | process.env.REWARD_TOKEN, 3 | 500, // 5% 4 | 100, // platform fee 1%, 5 | process.env.ACP_PLATFORM_TREASURY 6 | ]; -------------------------------------------------------------------------------- /scripts/arguments/aeroAdaptor.js: -------------------------------------------------------------------------------- 1 | module.exports = [ 2 | process.env.AERO_ROUTER, 3 | process.env.TAX_TOKEN, 4 | process.env.ASSET_TOKEN, 5 | process.env.AERO_FACTORY 6 | ]; -------------------------------------------------------------------------------- /scripts/arguments/bmwArguments.js: -------------------------------------------------------------------------------- 1 | module.exports = [process.env.DEPLOYER]; 2 | -------------------------------------------------------------------------------- /scripts/arguments/bmwChild.js: -------------------------------------------------------------------------------- 1 | module.exports = [process.env.CHILD_TUNNEL]; 2 | -------------------------------------------------------------------------------- /scripts/arguments/bondingTax.js: -------------------------------------------------------------------------------- 1 | module.exports = [ 2 | process.env.ADMIN, 3 | process.env.ASSET_TOKEN, 4 | process.env.TAX_TOKEN, 5 | process.env.ASSET_ROUTER, 6 | process.env.FROUTER, 7 | process.env.ASSET_VAULT, 8 | process.env.MIN_SWAP_THRESHOLD, 9 | process.env.MAX_SWAP_THRESHOLD 10 | ]; 11 | -------------------------------------------------------------------------------- /scripts/arguments/contributionNft.js: -------------------------------------------------------------------------------- 1 | module.exports = [process.env.VIRTUAL_NFT]; 2 | -------------------------------------------------------------------------------- /scripts/arguments/elo.js: -------------------------------------------------------------------------------- 1 | module.exports = [process.env.ADMIN]; 2 | -------------------------------------------------------------------------------- /scripts/arguments/fgenesis.js: -------------------------------------------------------------------------------- 1 | module.exports = [ 2 | process.env.BRIDGED_TOKEN, 3 | process.env.GENESIS_RESERVE_AMOUNT, 4 | process.env.GENESIS_MAX_CONTRIBUTION_VIRTUAL_AMOUNT, 5 | process.env.GENESIS_CREATION_FEE_ADDRESS, 6 | process.env.GENESIS_CREATION_FEE_AMOUNT, 7 | process.env.GENESIS_DURATION, 8 | process.env.GENESIS_TBA_SALT, 9 | process.env.TBA_IMPLEMENTATION, 10 | process.env.GENESIS_VOTING_PERIOD, 11 | process.env.GENESIS_DAO_THRESHOLD, 12 | process.env.FACTORY_V3, 13 | process.env.GENESIS_AGENT_TOKEN_TOTAL_SUPPLY, 14 | process.env.GENESIS_AGENT_TOKEN_LP_SUPPLY, 15 | ]; 16 | -------------------------------------------------------------------------------- /scripts/arguments/genesisDaoArguments.js: -------------------------------------------------------------------------------- 1 | module.exports = [ 2 | process.env.VOTING_TOKEN, 3 | process.env.GENESIS_VOTING_DELAY, 4 | process.env.GENESIS_VOTING_PERIOD, 5 | process.env.GENESIS_PROPOSAL_THRESHOLD 6 | ]; 7 | -------------------------------------------------------------------------------- /scripts/arguments/gov2.js: -------------------------------------------------------------------------------- 1 | module.exports = [ 2 | process.env.VEVIRTUAL, 3 | process.env.PROTOCOL_VOTING_DELAY, 4 | process.env.PROTOCOL_VOTING_PERIOD, 5 | process.env.PROTOCOL_PROPOSAL_THRESHOLD, 6 | process.env.PROTOCOL_QUORUM_NUMERATOR, 7 | process.env.OP, 8 | ]; 9 | -------------------------------------------------------------------------------- /scripts/arguments/govArguments.js: -------------------------------------------------------------------------------- 1 | module.exports = [ 2 | process.env.VOTING_TOKEN, 3 | process.env.PROTOCOL_VOTING_DELAY, 4 | process.env.PROTOCOL_VOTING_PERIOD, 5 | process.env.PROTOCOL_PROPOSAL_THRESHOLD, 6 | process.env.PROTOCOL_QUORUM_NUMERATOR, 7 | ]; 8 | -------------------------------------------------------------------------------- /scripts/arguments/inference.js: -------------------------------------------------------------------------------- 1 | module.exports = [ 2 | process.env.DEPLOYER, 3 | process.env.BRIDGED_TOKEN, 4 | process.env.VIRTUAL_NFT 5 | ]; 6 | -------------------------------------------------------------------------------- /scripts/arguments/lockArguments.js: -------------------------------------------------------------------------------- 1 | module.exports = ["xLP", "xLP", process.env.LP_TREASURY]; 2 | -------------------------------------------------------------------------------- /scripts/arguments/minter.js: -------------------------------------------------------------------------------- 1 | module.exports = [ 2 | process.env.SERVICE_NFT, 3 | process.env.CONTRIBUTION_NFT, 4 | process.env.VIRTUAL_NFT, 5 | process.env.IP_SHARES, 6 | process.env.IMPACT_MULTIPLIER, 7 | process.env.IP_VAULT, 8 | process.env.VIRTUAL_FACTORY, 9 | process.env.ADMIN, 10 | process.env.MAX_IMPACT, 11 | ]; -------------------------------------------------------------------------------- /scripts/arguments/nft.js: -------------------------------------------------------------------------------- 1 | module.exports = [process.env.ADMIN]; 2 | -------------------------------------------------------------------------------- /scripts/arguments/personaFactoryArguments.js: -------------------------------------------------------------------------------- 1 | module.exports = [ 2 | process.env.VIRTUAL_TOKEN_IMPL, 3 | process.env.VIRTUAL_VETOKEN_IMPL, 4 | process.env.VIRTUAL_DAO_IMPL, 5 | process.env.TBA, 6 | process.env.BRIDGED_TOKEN, 7 | process.env.VIRTUAL_NFT, 8 | process.env.VIRTUAL_APPLICATION_THRESHOLD, 9 | process.env.VIRTUAL_VAULT, 10 | ]; 11 | -------------------------------------------------------------------------------- /scripts/arguments/rewardTreasuryArguments.js: -------------------------------------------------------------------------------- 1 | module.exports = [process.env.ADMIN, process.env.REWARD_TOKEN, process.env.PERSONA_REWARD]; 2 | -------------------------------------------------------------------------------- /scripts/arguments/rewardsV2.js: -------------------------------------------------------------------------------- 1 | module.exports = [ 2 | process.env.REWARD_TOKEN, 3 | process.env.VIRTUAL_NFT, 4 | { 5 | protocolShares: process.env.PROTOCOL_SHARES, 6 | stakerShares: process.env.STAKER_SHARES, 7 | }, 8 | ]; 9 | -------------------------------------------------------------------------------- /scripts/arguments/serviceNft.js: -------------------------------------------------------------------------------- 1 | module.exports = [process.env.VIRTUAL_NFT, process.env.CONTRIBUTION_NFT, process.env.DATASET_SHARES]; 2 | -------------------------------------------------------------------------------- /scripts/arguments/staking.js: -------------------------------------------------------------------------------- 1 | module.exports = [process.env.BRIDGED_TOKEN, 104]; 2 | -------------------------------------------------------------------------------- /scripts/arguments/taxSwapper.js: -------------------------------------------------------------------------------- 1 | module.exports = [ 2 | process.env.BRIDGED_TOKEN, 3 | process.env.AGENT_TAX_MANAGER, 4 | process.env.UNISWAP_ROUTER, 5 | process.env.ADMIN, 6 | "1000000000000000000000000", 7 | ]; 8 | -------------------------------------------------------------------------------- /scripts/arguments/tbaBonus.js: -------------------------------------------------------------------------------- 1 | module.exports = [process.env.DEPLOYER, process.env.ASSET_TOKEN]; 2 | -------------------------------------------------------------------------------- /scripts/arguments/veTokenArguments.js: -------------------------------------------------------------------------------- 1 | module.exports = [process.env.DEPLOYER]; 2 | -------------------------------------------------------------------------------- /scripts/arguments/vipFactory.js: -------------------------------------------------------------------------------- 1 | module.exports = [ 2 | process.env.VIRTUAL_TOKEN_IMPL, 3 | process.env.VIRTUAL_VETOKEN_IMPL, 4 | process.env.VIRTUAL_DAO_IMPL, 5 | process.env.TBA, 6 | process.env.BRIDGED_TOKEN, 7 | process.env.VIRTUAL_NFT, 8 | process.env.VIRTUAL_APPLICATION_THRESHOLD_VIP, 9 | process.env.VIRTUAL_VAULT, 10 | ]; 11 | -------------------------------------------------------------------------------- /scripts/deployDefender.ts: -------------------------------------------------------------------------------- 1 | import { ethers, upgrades } from "hardhat"; 2 | 3 | (async () => { 4 | try { 5 | const args = [process.env.ADMIN, 4, process.env.VEVIRTUAL]; 6 | const Contract = await ethers.getContractFactory("Defender"); 7 | const contract = await upgrades.deployProxy(Contract, args, { 8 | initialOwner: process.env.CONTRACT_CONTROLLER, 9 | }); 10 | await contract.waitForDeployment(); 11 | console.log("Defender deployed to:", contract.target); 12 | } catch (e) { 13 | console.log(e); 14 | } 15 | })(); 16 | -------------------------------------------------------------------------------- /scripts/deployGovernance2.ts: -------------------------------------------------------------------------------- 1 | import { ethers } from "hardhat"; 2 | 3 | (async () => { 4 | try { 5 | const args = require("./arguments/gov2"); 6 | 7 | const dao = await ethers.deployContract("VirtualProtocolDAOV2", args); 8 | 9 | console.log("VirtualProtocolDAO deployed to:", dao.target); 10 | } catch (e) { 11 | console.log(e); 12 | } 13 | })(); 14 | -------------------------------------------------------------------------------- /scripts/deployTaxSwapper.ts: -------------------------------------------------------------------------------- 1 | import { ethers, upgrades } from "hardhat"; 2 | 3 | 4 | (async () => { 5 | try { 6 | const args = require("../arguments/taxSwapper"); 7 | const contract = await ethers.deployContract("TaxSwapper", args); 8 | await contract.waitForDeployment(); 9 | console.log("Swapper deployed to:", contract.target); 10 | } catch (e) { 11 | console.log(e); 12 | } 13 | })(); 14 | -------------------------------------------------------------------------------- /scripts/deployVeVirtual.ts: -------------------------------------------------------------------------------- 1 | import { ethers, upgrades } from "hardhat"; 2 | 3 | (async () => { 4 | try { 5 | const args = [process.env.BRIDGED_TOKEN, 108]; 6 | const Contract = await ethers.getContractFactory("veVirtual"); 7 | const contract = await upgrades.deployProxy(Contract, args, { 8 | initialOwner: process.env.CONTRACT_CONTROLLER, 9 | }); 10 | await contract.waitForDeployment(); 11 | console.log("veVirtual deployed to:", contract.target); 12 | } catch (e) { 13 | console.log(e); 14 | } 15 | })(); 16 | -------------------------------------------------------------------------------- /scripts/genesis/deployGenesis.ts: -------------------------------------------------------------------------------- 1 | import { parseEther } from "ethers"; 2 | import { ethers, upgrades } from "hardhat"; 3 | 4 | // const adminSigner = new ethers.Wallet( 5 | // process.env.ADMIN_PRIVATE_KEY, 6 | // ethers.provider 7 | // ); 8 | 9 | (async () => { 10 | try { 11 | // Basic check for .env variables 12 | const deployer = process.env.DEPLOYER; 13 | if (!deployer) { 14 | throw new Error("DEPLOYER not set in environment"); 15 | } 16 | const beOpsWallet = process.env.GENESIS_BE_OPS_WALLET; 17 | if (!beOpsWallet) { 18 | throw new Error("GENESIS_BE_OPS_WALLET not set in environment"); 19 | } 20 | const contractController = process.env.CONTRACT_CONTROLLER; 21 | if (!contractController) { 22 | throw new Error("CONTRACT_CONTROLLER not set in environment"); 23 | } 24 | const admin = process.env.ADMIN; 25 | if (!admin) { 26 | throw new Error("ADMIN not set in environment"); 27 | } 28 | 29 | // Load arguments from the arguments file 30 | const args = require("../arguments/fgenesis"); 31 | 32 | // Create the params struct 33 | const params = { 34 | virtualToken: args[0], 35 | reserve: args[1], 36 | maxContribution: args[2], 37 | feeAddr: args[3], 38 | feeAmt: args[4], 39 | duration: args[5], 40 | tbaSalt: args[6], 41 | tbaImpl: args[7], 42 | votePeriod: args[8], 43 | threshold: args[9], 44 | agentFactory: args[10], 45 | agentTokenTotalSupply: args[11], 46 | agentTokenLpSupply: args[12], 47 | }; 48 | 49 | console.log("Deploying FGenesis Proxy with params:", params); 50 | 51 | const FGenesis = await ethers.getContractFactory("FGenesis"); 52 | const fGenesis = await upgrades.deployProxy( 53 | FGenesis, 54 | [params], 55 | { 56 | initialOwner: process.env.CONTRACT_CONTROLLER, 57 | } 58 | ); 59 | await fGenesis.waitForDeployment(); 60 | 61 | const deployedFGenesisAddress = await fGenesis.getAddress(); 62 | console.log("FGenesis Proxy deployed to:", deployedFGenesisAddress); 63 | 64 | // Reason: because Admin need to have the rights to call setParams for the fgenesis proxy 65 | const tx1 = await fGenesis.grantRole( 66 | await fGenesis.DEFAULT_ADMIN_ROLE(), 67 | process.env.ADMIN 68 | ); 69 | await tx1.wait(); 70 | console.log("Granted DEFAULT_ADMIN_ROLE of fGenesis Proxy to Admin: ", process.env.ADMIN); 71 | 72 | // Reason: because Admin need to have the rights to call withdrawLeftAssetsAfterFinalized for the fgenesis proxy 73 | const tx2 = await fGenesis.grantRole( 74 | await fGenesis.ADMIN_ROLE(), 75 | process.env.ADMIN 76 | ); 77 | await tx2.wait(); 78 | console.log("Granted ADMIN_ROLE of fGenesis Proxy to Admin: ", process.env.ADMIN); 79 | 80 | // Reason: because BE Ops need to have the rights to call onGenesisSuccess and onGenesisFailed for the fgenesis proxy 81 | const tx3 = await fGenesis.grantRole( 82 | await fGenesis.OPERATION_ROLE(), 83 | process.env.GENESIS_BE_OPS_WALLET 84 | ); 85 | await tx3.wait(); 86 | console.log("Granted OPERATION_ROLE of fGenesis Proxy to BE Ops: ", process.env.GENESIS_BE_OPS_WALLET); 87 | 88 | // Get the existed AgentFactory contract instance 89 | const agentFactory = await ethers.getContractAt( 90 | "AgentFactoryV3", 91 | params.agentFactory 92 | ); 93 | 94 | // Reason: because FGenesis Proxy need to have the rights to grant BONDING_ROLE to the new Genesis contract 95 | const tx4 = await agentFactory.grantRole( 96 | await agentFactory.DEFAULT_ADMIN_ROLE(), 97 | deployedFGenesisAddress 98 | ); 99 | await tx4.wait(); 100 | console.log("Granted DEFAULT_ADMIN_ROLE of AgentFactory to FGenesis Proxy: ", deployedFGenesisAddress); 101 | 102 | // reason: there is no need for deployer to have ADMIN_ROLE 103 | const tx5 = await fGenesis.revokeRole( 104 | await fGenesis.ADMIN_ROLE(), 105 | process.env.DEPLOYER 106 | ); 107 | await tx5.wait(); 108 | console.log("Revoked ADMIN_ROLE of fGenesis Proxy from Deployer: ", process.env.DEPLOYER); 109 | 110 | // reason: there is no need for deployer to have DEFAULT_ADMIN_ROLE 111 | const tx6 = await fGenesis.revokeRole( 112 | await fGenesis.DEFAULT_ADMIN_ROLE(), 113 | process.env.DEPLOYER 114 | ); 115 | await tx6.wait(); 116 | console.log("Revoked DEFAULT_ADMIN_ROLE of fGenesis Proxy from Deployer: ", process.env.DEPLOYER); 117 | 118 | // Print deployed parameters 119 | const deployedParams = await fGenesis.params(); 120 | console.log("\nDeployed contract parameters:"); 121 | console.log(deployedParams); 122 | 123 | console.log("Deployment and role setup completed"); 124 | } catch (e) { 125 | console.error("Deployment failed:", e); 126 | throw e; 127 | } 128 | })(); 129 | -------------------------------------------------------------------------------- /scripts/society/deployACP.ts: -------------------------------------------------------------------------------- 1 | import { ethers, upgrades } from "hardhat"; 2 | 3 | (async () => { 4 | try { 5 | const args = require("../arguments/acpArguments"); 6 | const Contract = await ethers.getContractFactory("ACPSimple"); 7 | const contract = await upgrades.deployProxy(Contract, args, { 8 | initialOwner: process.env.CONTRACT_CONTROLLER, 9 | }); 10 | await contract.waitForDeployment(); 11 | console.log("ACPSimple deployed to:", contract.target); 12 | } catch (e) { 13 | console.log(e); 14 | } 15 | })(); 16 | -------------------------------------------------------------------------------- /scripts/v1/deployContributions.ts: -------------------------------------------------------------------------------- 1 | import { ethers, upgrades } from "hardhat"; 2 | 3 | (async () => { 4 | try { 5 | const args = require("../arguments/contributionNft"); 6 | const Contribution = await ethers.getContractFactory("ContributionNft"); 7 | const contribution = await upgrades.deployProxy( 8 | Contribution, 9 | args, 10 | { initialOwner: process.env.CONTRACT_CONTROLLER } 11 | ); 12 | const contributionAddress = await contribution.getAddress(); 13 | await contribution.setAdmin(process.env.ADMIN); 14 | console.log("ContributionNft deployed to:", contributionAddress); 15 | } catch (e) { 16 | console.log(e); 17 | } 18 | })(); 19 | -------------------------------------------------------------------------------- /scripts/v1/deployGovernance.ts: -------------------------------------------------------------------------------- 1 | import { ethers } from "hardhat"; 2 | 3 | (async () => { 4 | try { 5 | const dao = await ethers.deployContract("VirtualProtocolDAO", [ 6 | process.env.VOTING_TOKEN, 7 | process.env.PROTOCOL_VOTING_DELAY, 8 | process.env.PROTOCOL_VOTING_PERIOD, 9 | process.env.PROTOCOL_PROPOSAL_THRESHOLD, 10 | process.env.PROTOCOL_QUORUM_NUMERATOR 11 | ]); 12 | 13 | console.log("VirtualProtocolDAO deployed to:", dao.target); 14 | } catch (e) { 15 | console.log(e); 16 | } 17 | })(); 18 | -------------------------------------------------------------------------------- /scripts/v1/deployPersonaFactory.ts: -------------------------------------------------------------------------------- 1 | import { ethers, upgrades } from "hardhat"; 2 | const deployParams = require("../arguments/personaFactoryArguments.js"); 3 | 4 | (async () => { 5 | try { 6 | const AgentFactory = await ethers.getContractFactory("AgentFactory"); 7 | const factory = await upgrades.deployProxy(AgentFactory, deployParams); 8 | await factory.waitForDeployment(); 9 | 10 | const factoryAddress = await factory.getAddress(); 11 | 12 | console.log("AgentFactory deployed to:", factoryAddress); 13 | 14 | // Grant factory to mint NFTs 15 | const adminSigner = new ethers.Wallet( 16 | process.env.ADMIN_PRIVATE_KEY, 17 | ethers.provider 18 | ); 19 | const nft = await ethers.getContractAt("AgentNft", process.env.VIRTUAL_NFT, adminSigner); 20 | await nft.grantRole(ethers.id("MINTER_ROLE"), factoryAddress); 21 | } catch (e) { 22 | console.log(e); 23 | } 24 | })(); 25 | -------------------------------------------------------------------------------- /scripts/v1/deployReward.ts: -------------------------------------------------------------------------------- 1 | import { ethers, upgrades } from "hardhat"; 2 | 3 | (async () => { 4 | try { 5 | const Contract = await ethers.getContractFactory("AgentReward"); 6 | const contract = await upgrades.deployProxy( 7 | Contract, 8 | [ 9 | process.env.REWARD_TOKEN, 10 | process.env.VIRTUAL_NFT, 11 | process.env.CONTRIBUTION_NFT, 12 | process.env.SERVICE_NFT, 13 | { 14 | protocolShares: process.env.PROTOCOL_SHARES, 15 | contributorShares: process.env.CONTRIBUTOR_SHARES, 16 | stakerShares: process.env.STAKER_SHARES, 17 | parentShares: process.env.PARENT_SHARES, 18 | stakeThreshold: process.env.REWARD_STAKE_THRESHOLD, 19 | }, 20 | ], 21 | { initialOwner: process.env.CONTRACT_CONTROLLER } 22 | ); 23 | await contract.waitForDeployment(); 24 | const address = await contract.getAddress() 25 | 26 | console.log("AgentReward deployed to:", address); 27 | } catch (e) { 28 | console.log(e); 29 | } 30 | })(); 31 | -------------------------------------------------------------------------------- /scripts/v1/deployRewardTreasury.ts: -------------------------------------------------------------------------------- 1 | import { ethers } from "hardhat"; 2 | const deployArguments = require("../arguments/rewardTreasuryArguments"); 3 | 4 | (async () => { 5 | try { 6 | const contract = await ethers.deployContract( 7 | "RewardTreasury", 8 | deployArguments 9 | ); 10 | await contract.waitForDeployment(); 11 | 12 | console.log(`Reward Treasury Contract deployed to ${contract.target}`); 13 | } catch (e) { 14 | console.log(e); 15 | } 16 | })(); 17 | -------------------------------------------------------------------------------- /scripts/v1/deployRewardV2.ts: -------------------------------------------------------------------------------- 1 | import { ethers, upgrades } from "hardhat"; 2 | 3 | (async () => { 4 | try { 5 | const contract = await ethers.deployContract("AgentRewardV2") 6 | await contract.waitForDeployment(); 7 | await contract.initialize( 8 | process.env.REWARD_TOKEN, 9 | process.env.VIRTUAL_NFT, 10 | process.env.CONTRIBUTION_NFT, 11 | process.env.SERVICE_NFT, 12 | { 13 | protocolShares: process.env.PROTOCOL_SHARES, 14 | contributorShares: process.env.CONTRIBUTOR_SHARES, 15 | stakerShares: process.env.STAKER_SHARES, 16 | parentShares: process.env.PARENT_SHARES, 17 | stakeThreshold: process.env.REWARD_STAKE_THRESHOLD, 18 | }, 19 | ) 20 | const address = await contract.getAddress() 21 | 22 | console.log("AgentRewardX2 deployed to:", address); 23 | } catch (e) { 24 | console.log(e); 25 | } 26 | })(); 27 | -------------------------------------------------------------------------------- /scripts/v1/deployService.ts: -------------------------------------------------------------------------------- 1 | import { ethers, upgrades } from "hardhat"; 2 | 3 | (async () => { 4 | try { 5 | const args = require("../arguments/serviceNft"); 6 | 7 | const Service = await ethers.getContractFactory("ServiceNft"); 8 | const service = await upgrades.deployProxy(Service, args, { 9 | initialOwner: process.env.CONTRACT_CONTROLLER, 10 | }); 11 | const serviceAddress = await service.getAddress(); 12 | console.log("ServiceNft deployed to:", serviceAddress); 13 | await service.transferOwnership(process.env.ADMIN); 14 | 15 | const adminSigner = new ethers.Wallet( 16 | process.env.ADMIN_PRIVATE_KEY, 17 | ethers.provider 18 | ); 19 | const nft = await ethers.getContractAt( 20 | "AgentNft", 21 | process.env.VIRTUAL_NFT, 22 | adminSigner 23 | ); 24 | await nft.setContributionService(process.env.CONTRIBUTION_NFT, serviceAddress); 25 | } catch (e) { 26 | console.log(e); 27 | } 28 | })(); 29 | -------------------------------------------------------------------------------- /scripts/v1/deployTimeLock.ts: -------------------------------------------------------------------------------- 1 | import { ethers } from "hardhat"; 2 | const deployArguments = require("../arguments/lockArguments"); 3 | 4 | (async () => { 5 | try { 6 | const contract = await ethers.deployContract( 7 | "TimeLock", 8 | deployArguments 9 | ); 10 | await contract.waitForDeployment(); 11 | await contract.grantRole(await contract.GOV_ROLE(), process.env.DAO) 12 | await contract.grantRole(await contract.ADMIN_ROLE(), process.env.ADMIN) 13 | await contract.grantRole(await contract.DEFAULT_ADMIN_ROLE(), process.env.ADMIN) 14 | await contract.renounceRole(await contract.DEFAULT_ADMIN_ROLE(), process.env.DEPLOYER) 15 | 16 | console.log(`Staking Contract deployed to ${contract.target}`); 17 | } catch (e) { 18 | console.log(e); 19 | } 20 | })(); 21 | -------------------------------------------------------------------------------- /scripts/v1/deployVetoken.ts: -------------------------------------------------------------------------------- 1 | import { ethers } from "hardhat"; 2 | 3 | (async () => { 4 | try { 5 | const { getNamedAccounts } = hre; 6 | const { deployer } = await getNamedAccounts(); 7 | const token = await ethers.deployContract( 8 | "veVirtualToken", 9 | [deployer], 10 | {} 11 | ); 12 | console.log("veVirtualToken deployed to:", token.target); 13 | } catch (e) { 14 | console.log(e); 15 | } 16 | })(); 17 | -------------------------------------------------------------------------------- /scripts/v1/deployVirtualGenesis.ts: -------------------------------------------------------------------------------- 1 | import { ethers } from "hardhat"; 2 | 3 | const deployParams = require("../arguments/genesisDaoArguments.js"); 4 | 5 | (async () => { 6 | try { 7 | const dao = await ethers.deployContract("VirtualGenesisDAO", deployParams); 8 | 9 | console.log("VirtualGenesisDAO deployed to:", dao.target); 10 | } catch (e) { 11 | console.log(e); 12 | } 13 | })(); 14 | -------------------------------------------------------------------------------- /scripts/v1/deployVirtualImplementations.ts: -------------------------------------------------------------------------------- 1 | import { ethers } from "hardhat"; 2 | 3 | (async () => { 4 | try { 5 | const dao = await ethers.deployContract("AgentDAO"); 6 | await dao.waitForDeployment(); 7 | console.log("AgentDAO deployed to:", dao.target); 8 | 9 | const token = await ethers.deployContract("AgentToken"); 10 | await token.waitForDeployment(); 11 | console.log("AgentToken deployed to:", token.target); 12 | } catch (e) { 13 | console.log(e); 14 | } 15 | })(); 16 | -------------------------------------------------------------------------------- /scripts/v1/deployVirtualNft.ts: -------------------------------------------------------------------------------- 1 | import { ethers, upgrades } from "hardhat"; 2 | 3 | const deployParams = require("../arguments/nft.js"); 4 | 5 | (async () => { 6 | try { 7 | const AgentNft = await ethers.getContractFactory("AgentNft"); 8 | const nft = await upgrades.deployProxy(AgentNft, deployParams, { 9 | initialOwner: process.env.CONTRACT_CONTROLLER, 10 | }); 11 | console.log("AgentNft deployed to:", nft.target); 12 | } catch (e) { 13 | console.log(e); 14 | } 15 | })(); 16 | -------------------------------------------------------------------------------- /scripts/v1/upgradeFactory.ts: -------------------------------------------------------------------------------- 1 | import { ethers, upgrades } from "hardhat"; 2 | 3 | (async () => { 4 | try { 5 | const AgentFactory = await ethers.getContractFactory("AgentFactory"); 6 | const factory = await upgrades.upgradeProxy( 7 | process.env.VIRTUAL_FACTORY, 8 | AgentFactory 9 | ); 10 | await factory.setImplementations( 11 | process.env.VIRTUAL_TOKEN_IMPL, 12 | process.env.VIRTUAL_DAO_IMPL 13 | ); 14 | console.log("Upgraded", factory.target); 15 | } catch (e) { 16 | console.log(e); 17 | } 18 | })(); 19 | -------------------------------------------------------------------------------- /scripts/v2/configureTBABonus.ts: -------------------------------------------------------------------------------- 1 | import { ethers, upgrades } from "hardhat"; 2 | 3 | (async () => { 4 | try { 5 | const Contract = await ethers.getContractFactory("AgentTax"); 6 | 7 | const contract = await ethers.getContractAt( 8 | "AgentTax", 9 | process.env.AGENT_TAX_MANAGER 10 | ); 11 | await contract.updateTbaBonus(process.env.TBABONUS); 12 | 13 | console.log("TBABonus address updated"); 14 | } catch (e) { 15 | console.log(e); 16 | } 17 | })(); 18 | -------------------------------------------------------------------------------- /scripts/v2/deployAeroAdaptor.ts: -------------------------------------------------------------------------------- 1 | import { ethers, upgrades } from "hardhat"; 2 | 3 | const adminSigner = new ethers.Wallet( 4 | process.env.ADMIN_PRIVATE_KEY, 5 | ethers.provider 6 | ); 7 | 8 | (async () => { 9 | try { 10 | const args = require("../arguments/aeroAdaptor"); 11 | const contract = await ethers.deployContract("AeroAdaptor", args); 12 | await contract.waitForDeployment(); 13 | console.log("AeroAdaptor deployed to:", contract.target); 14 | } catch (e) { 15 | console.log(e); 16 | } 17 | })(); 18 | -------------------------------------------------------------------------------- /scripts/v2/deployBondingTax.ts: -------------------------------------------------------------------------------- 1 | import { ethers, upgrades } from "hardhat"; 2 | 3 | const adminSigner = new ethers.Wallet( 4 | process.env.ADMIN_PRIVATE_KEY, 5 | ethers.provider 6 | ); 7 | 8 | (async () => { 9 | try { 10 | const args = require("../arguments/bondingTax"); 11 | const Contract = await ethers.getContractFactory("BondingTax"); 12 | const contract = await upgrades.deployProxy(Contract, args, { 13 | initialOwner: process.env.CONTRACT_CONTROLLER, 14 | }); 15 | await contract.waitForDeployment(); 16 | console.log("BondingTax deployed to:", contract.target); 17 | } catch (e) { 18 | console.log(e); 19 | } 20 | })(); 21 | -------------------------------------------------------------------------------- /scripts/v2/deployElo.ts: -------------------------------------------------------------------------------- 1 | import { ethers, upgrades } from "hardhat"; 2 | 3 | const adminSigner = new ethers.Wallet( 4 | process.env.ADMIN_PRIVATE_KEY, 5 | ethers.provider 6 | ); 7 | 8 | (async () => { 9 | try { 10 | const args = require("../arguments/elo"); 11 | const Contract = await ethers.getContractFactory("EloCalculator"); 12 | const contract = await upgrades.deployProxy(Contract, args, { 13 | initialOwner: process.env.CONTRACT_CONTROLLER, 14 | }); 15 | await contract.waitForDeployment(); 16 | console.log("EloCalculator deployed to:", contract.target); 17 | } catch (e) { 18 | console.log(e); 19 | } 20 | })(); 21 | -------------------------------------------------------------------------------- /scripts/v2/deployFactory.ts: -------------------------------------------------------------------------------- 1 | import { parseEther } from "ethers"; 2 | import { ethers, upgrades } from "hardhat"; 3 | 4 | const adminSigner = new ethers.Wallet( 5 | process.env.ADMIN_PRIVATE_KEY, 6 | ethers.provider 7 | ); 8 | 9 | (async () => { 10 | try { 11 | const args = require("../arguments/personaFactoryArguments"); 12 | const Contract = await ethers.getContractFactory("AgentFactoryV2"); 13 | const contract = await upgrades.deployProxy(Contract, args, { 14 | initialOwner: process.env.CONTRACT_CONTROLLER, 15 | }); 16 | console.log("AgentFactoryV2 deployed to:", contract.target); 17 | 18 | const t = await contract.setTokenAdmin(process.env.ADMIN); 19 | await t.wait(); 20 | const t2 = await contract.setTokenSupplyParams( 21 | process.env.AGENT_TOKEN_SUPPLY, 22 | process.env.AGENT_TOKEN_LP_SUPPLY, 23 | process.env.AGENT_TOKEN_VAULT_SUPPLY, 24 | process.env.AGENT_TOKEN_LIMIT_WALLET, 25 | process.env.AGENT_TOKEN_LIMIT_TRX, 26 | process.env.BOT_PROTECTION, 27 | process.env.MINTER 28 | ); 29 | await t2.wait(); 30 | const t3 = await contract.setTokenTaxParams( 31 | process.env.TAX, 32 | process.env.TAX, 33 | process.env.SWAP_THRESHOLD, 34 | process.env.TAX_VAULT 35 | ); 36 | await t3.wait(); 37 | const t4 = await contract.setUniswapRouter(process.env.UNISWAP_ROUTER); 38 | await t4.wait(); 39 | const t5 = await contract.grantRole( 40 | await contract.WITHDRAW_ROLE(), 41 | process.env.OP 42 | ); 43 | await t5.wait(); 44 | 45 | const nft = await ethers.getContractAt( 46 | "AgentNftV2", 47 | process.env.VIRTUAL_NFT, 48 | adminSigner 49 | ); 50 | await nft.grantRole(await nft.MINTER_ROLE(), contract.target); 51 | } catch (e) { 52 | console.log(e); 53 | } 54 | })(); 55 | -------------------------------------------------------------------------------- /scripts/v2/deployInference.ts: -------------------------------------------------------------------------------- 1 | import { ethers, upgrades } from "hardhat"; 2 | 3 | const adminSigner = new ethers.Wallet( 4 | process.env.ADMIN_PRIVATE_KEY, 5 | ethers.provider 6 | ); 7 | 8 | (async () => { 9 | try { 10 | const args = require("../arguments/inference"); 11 | const Contract = await ethers.getContractFactory("AgentInference"); 12 | const contract = await upgrades.deployProxy(Contract, args, { 13 | initialOwner: process.env.CONTRACT_CONTROLLER, 14 | }); 15 | await contract.waitForDeployment(); 16 | console.log("AgentInference deployed to:", contract.target); 17 | } catch (e) { 18 | console.log(e); 19 | } 20 | })(); 21 | -------------------------------------------------------------------------------- /scripts/v2/deployMigrator.ts: -------------------------------------------------------------------------------- 1 | import { ethers, upgrades } from "hardhat"; 2 | 3 | const adminSigner = new ethers.Wallet( 4 | process.env.ADMIN_PRIVATE_KEY, 5 | ethers.provider 6 | ); 7 | 8 | (async () => { 9 | try { 10 | const contract = await ethers.deployContract("AgentMigrator", [process.env.VIRTUAL_NFT]); 11 | await contract.waitForDeployment(); 12 | console.log("AgentMigrator deployed to:", contract.target); 13 | 14 | await contract.setInitParams(process.env.ADMIN, process.env.BRIDGED_TOKEN, process.env.UNISWAP_ROUTER, process.env.VIRTUAL_APPLICATION_THRESHOLD, process.env.MATURITY_DURATION) 15 | await contract.setTokenSupplyParams( 16 | process.env.AGENT_TOKEN_LIMIT, 17 | process.env.AGENT_TOKEN_LP_SUPPLY, 18 | process.env.AGENT_TOKEN_VAULT_SUPPLY, 19 | process.env.AGENT_TOKEN_LIMIT, 20 | process.env.AGENT_TOKEN_LIMIT, 21 | process.env.BOT_PROTECTION, 22 | process.env.MINTER 23 | ); 24 | await contract.setTokenTaxParams( 25 | process.env.TAX, 26 | process.env.TAX, 27 | process.env.SWAP_THRESHOLD, 28 | process.env.TAX_VAULT 29 | ); 30 | await contract.setImplementations( 31 | process.env.VIRTUAL_TOKEN_IMPL, 32 | process.env.VIRTUAL_VETOKEN_IMPL, 33 | process.env.VIRTUAL_DAO_IMPL 34 | ); 35 | 36 | const nft = await ethers.getContractAt("AgentNftV2", process.env.VIRTUAL_NFT, adminSigner); 37 | await nft.grantRole(await nft.ADMIN_ROLE(), contract.target); 38 | } catch (e) { 39 | console.log(e); 40 | } 41 | })(); 42 | -------------------------------------------------------------------------------- /scripts/v2/deployMinter.ts: -------------------------------------------------------------------------------- 1 | import { ethers, upgrades } from "hardhat"; 2 | 3 | const adminSigner = new ethers.Wallet( 4 | process.env.ADMIN_PRIVATE_KEY, 5 | ethers.provider 6 | ); 7 | 8 | (async () => { 9 | try { 10 | const args = require("../arguments/minter"); 11 | const Contract = await ethers.getContractFactory("Minter"); 12 | const contract = await upgrades.deployProxy(Contract, args, { 13 | initialOwner: process.env.CONTRACT_CONTROLLER, 14 | }); 15 | await contract.waitForDeployment(); 16 | console.log("Minter deployed to:", contract.target); 17 | } catch (e) { 18 | console.log(e); 19 | } 20 | })(); 21 | -------------------------------------------------------------------------------- /scripts/v2/deployRewards.ts: -------------------------------------------------------------------------------- 1 | import { ethers, upgrades } from "hardhat"; 2 | 3 | const adminSigner = new ethers.Wallet( 4 | process.env.ADMIN_PRIVATE_KEY, 5 | ethers.provider 6 | ); 7 | 8 | (async () => { 9 | try { 10 | const args = require("../arguments/rewardsV2"); 11 | const Contract = await ethers.getContractFactory("AgentRewardV2"); 12 | const contract = await upgrades.deployProxy(Contract, args, { 13 | initialOwner: process.env.CONTRACT_CONTROLLER, 14 | }); 15 | await contract.waitForDeployment(); 16 | console.log("AgentRewardV2 deployed to:", contract.target); 17 | } catch (e) { 18 | console.log(e); 19 | } 20 | })(); 21 | -------------------------------------------------------------------------------- /scripts/v2/deployStaking.ts: -------------------------------------------------------------------------------- 1 | import { ethers, upgrades } from "hardhat"; 2 | 3 | (async () => { 4 | try { 5 | const args = require("../arguments/staking"); 6 | const Contract = await ethers.getContractFactory("veVirtual"); 7 | const contract = await upgrades.deployProxy(Contract, args, { 8 | initialOwner: process.env.CONTRACT_CONTROLLER, 9 | }); 10 | await contract.waitForDeployment(); 11 | console.log("veVIRTUAL deployed to:", contract.target); 12 | } catch (e) { 13 | console.log(e); 14 | } 15 | })(); 16 | -------------------------------------------------------------------------------- /scripts/v2/deployTBABonus.ts: -------------------------------------------------------------------------------- 1 | import { ethers, upgrades } from "hardhat"; 2 | 3 | (async () => { 4 | try { 5 | const args = require("../arguments/tbaBonus"); 6 | const Contract = await ethers.getContractFactory("TBABonus"); 7 | 8 | const contract = await upgrades.deployProxy(Contract, args, { 9 | initialOwner: process.env.CONTRACT_CONTROLLER, 10 | }); 11 | await contract.waitForDeployment(); 12 | console.log("TBABonus deployed to:", contract.target); 13 | 14 | await contract.grantRole( 15 | contract.EXECUTOR_ROLE(), 16 | process.env.AGENT_TAX_MANAGER 17 | ); 18 | } catch (e) { 19 | console.log(e); 20 | } 21 | })(); 22 | -------------------------------------------------------------------------------- /scripts/v2/deployVIPFactory.ts: -------------------------------------------------------------------------------- 1 | import { parseEther } from "ethers"; 2 | import { ethers, upgrades } from "hardhat"; 3 | 4 | const adminSigner = new ethers.Wallet( 5 | process.env.ADMIN_PRIVATE_KEY, 6 | ethers.provider 7 | ); 8 | 9 | (async () => { 10 | try { 11 | const args = require("../arguments/vipFactory"); 12 | const Contract = await ethers.getContractFactory("AgentFactoryV2"); 13 | const contract = await upgrades.deployProxy(Contract, args, { 14 | initialOwner: process.env.CONTRACT_CONTROLLER, 15 | }); 16 | console.log("AgentFactoryV2 deployed to:", contract.target); 17 | // const contract = await ethers.getContractAt( 18 | // "AgentFactoryV2", 19 | // process.env.VIRTUAL_FACTORY_PREMIUM 20 | // ); 21 | const t = await contract.setTokenAdmin(process.env.ADMIN); 22 | await t.wait(); 23 | const t2 = await contract.setTokenSupplyParams( 24 | process.env.AGENT_TOKEN_SUPPLY, 25 | process.env.AGENT_TOKEN_LP_SUPPLY, 26 | process.env.AGENT_TOKEN_VAULT_SUPPLY, 27 | process.env.AGENT_TOKEN_LIMIT_WALLET, 28 | process.env.AGENT_TOKEN_LIMIT_TRX, 29 | process.env.BOT_PROTECTION, 30 | process.env.MINTER 31 | ); 32 | await t2.wait(); 33 | const t3 = await contract.setTokenTaxParams( 34 | process.env.TAX, 35 | process.env.TAX, 36 | process.env.SWAP_THRESHOLD, 37 | process.env.TAX_VAULT 38 | ); 39 | await t3.wait(); 40 | const t4 = await contract.setUniswapRouter(process.env.UNISWAP_ROUTER); 41 | await t4.wait(); 42 | const t5 = await contract.grantRole(await contract.WITHDRAW_ROLE(), process.env.OP) 43 | await t5.wait() 44 | 45 | const nft = await ethers.getContractAt("AgentNftV2", process.env.VIRTUAL_NFT, adminSigner) 46 | await nft.grantRole(await nft.MINTER_ROLE(), process.env.VIRTUAL_FACTORY_PREMIUM) 47 | } catch (e) { 48 | console.log(e); 49 | } 50 | })(); 51 | -------------------------------------------------------------------------------- /scripts/v2/migrateRewards.ts: -------------------------------------------------------------------------------- 1 | import { ethers, upgrades } from "hardhat"; 2 | 3 | (async () => { 4 | try { 5 | const Factory = await ethers.getContractFactory("AgentRewardV2"); 6 | const contract = await upgrades.upgradeProxy( 7 | process.env.PERSONA_REWARD, 8 | Factory 9 | ); 10 | console.log("AgentRewardV2 upgraded to:", contract.target); 11 | } catch (e) { 12 | console.log(e); 13 | } 14 | })(); 15 | -------------------------------------------------------------------------------- /scripts/v2/migrateV2.ts: -------------------------------------------------------------------------------- 1 | import { ethers, upgrades } from "hardhat"; 2 | 3 | const adminSigner = new ethers.Wallet( 4 | process.env.ADMIN_PRIVATE_KEY, 5 | ethers.provider 6 | ); 7 | 8 | const deployerSigner = new ethers.Wallet( 9 | process.env.PRIVATE_KEY, 10 | ethers.provider 11 | ); 12 | 13 | async function upgradeFactory(implementations, assetToken) { 14 | const AgentFactory = await ethers.getContractFactory("AgentFactoryV2"); 15 | const factory = await upgrades.upgradeProxy( 16 | process.env.VIRTUAL_FACTORY, 17 | AgentFactory 18 | ); 19 | if (implementations) { 20 | await factory.setImplementations( 21 | implementations.tokenImpl.target, 22 | implementations.veTokenImpl.target, 23 | implementations.daoImpl.target 24 | ); 25 | } 26 | 27 | if (assetToken) { 28 | await factory.setAssetToken(assetToken); 29 | } 30 | await factory.setTokenAdmin(process.env.ADMIN); 31 | await factory.setTokenSupplyParams( 32 | process.env.AGENT_TOKEN_SUPPLY, 33 | process.env.AGENT_TOKEN_LP_SUPPLY, 34 | process.env.AGENT_TOKEN_VAULT_SUPPLY, 35 | process.env.AGENT_TOKEN_LIMIT, 36 | process.env.AGENT_TOKEN_LIMIT, 37 | process.env.BOT_PROTECTION, 38 | process.env.MINTER 39 | ); 40 | await factory.setTokenTaxParams( 41 | process.env.TAX, 42 | process.env.TAX, 43 | process.env.SWAP_THRESHOLD, 44 | process.env.TAX_VAULT 45 | ); 46 | 47 | console.log("Upgraded FactoryV2", factory.target); 48 | } 49 | 50 | async function importContract() { 51 | const ContractFactory = await ethers.getContractFactory( 52 | "AgentNftV2", 53 | adminSigner 54 | ); 55 | await upgrades.forceImport( 56 | "0x8f767259867Cc93df37e59ba445019418871ea57", 57 | ContractFactory 58 | ); 59 | } 60 | 61 | async function deployAgentNft() { 62 | const nft = await ethers.deployContract("AgentNftV2"); 63 | console.log("AgentNft deployed to:", nft.target); 64 | } 65 | 66 | async function updateAgentNftRoles() { 67 | const nft = await ethers.getContractAt( 68 | "AgentNftV2", 69 | process.env.VIRTUAL_NFT, 70 | adminSigner 71 | ); 72 | await nft.grantRole(ethers.id("ADMIN_ROLE"), process.env.ADMIN); 73 | console.log("Updated Admin Role"); 74 | } 75 | 76 | async function upgradeAgentNft() { 77 | const nft = await ethers.deployContract("AgentNftV2"); 78 | console.log("AgentNft deployed to:", nft.target); 79 | 80 | const proxyAdmin = await ethers.getContractAt( 81 | "MyProxyAdmin", 82 | process.env.VIRTUAL_NFT_PROXY, 83 | adminSigner 84 | ); 85 | await proxyAdmin.upgradeAndCall(process.env.VIRTUAL_NFT, nft.target, "0x"); 86 | console.log("Upgraded AgentNft"); 87 | 88 | const contract = await ethers.getContractAt( 89 | "AgentNftV2", 90 | process.env.VIRTUAL_NFT 91 | ); 92 | await contract.setEloCalculator(process.env.ELO); 93 | } 94 | 95 | async function deployImplementations() { 96 | const daoImpl = await ethers.deployContract("AgentDAO"); 97 | await daoImpl.waitForDeployment(); 98 | console.log("AgentDAO deployed to:", daoImpl.target); 99 | 100 | // const tokenImpl = await ethers.deployContract("AgentToken"); 101 | // await tokenImpl.waitForDeployment(); 102 | // console.log("AgentToken deployed to:", tokenImpl.target); 103 | 104 | // const veTokenImpl = await ethers.deployContract("AgentVeToken"); 105 | // await veTokenImpl.waitForDeployment(); 106 | // console.log("AgentVeToken deployed to:", veTokenImpl.target); 107 | 108 | return { daoImpl, tokenImpl: null, veTokenImpl: null }; 109 | } 110 | 111 | (async () => { 112 | try { 113 | //const implementations = await deployImplementations(); 114 | await upgradeFactory(); 115 | //await deployAgentNft(); 116 | //await upgradeAgentNft(); 117 | } catch (e) { 118 | console.log(e); 119 | } 120 | })(); 121 | -------------------------------------------------------------------------------- /scripts/v2/upgradeAgentTax.ts: -------------------------------------------------------------------------------- 1 | import { ethers, upgrades } from "hardhat"; 2 | 3 | (async () => { 4 | try { 5 | const Contract = await ethers.getContractFactory("AgentTax"); 6 | const contract = await upgrades.upgradeProxy( 7 | process.env.AGENT_TAX_MANAGER, 8 | Contract 9 | ); 10 | console.log("Contract upgraded:", contract.target); 11 | } catch (e) { 12 | console.log(e); 13 | } 14 | })(); 15 | -------------------------------------------------------------------------------- /scripts/v2/upgradeFRouter.ts: -------------------------------------------------------------------------------- 1 | import { ethers, upgrades } from "hardhat"; 2 | 3 | (async () => { 4 | try { 5 | const Contract = await ethers.getContractFactory("FRouter") 6 | const contract = await ethers.deployContract("FRouter"); 7 | console.log("Contract deployed to:", contract.target); 8 | 9 | const proxyAdmin = await ethers.getContractAt( 10 | "MyProxyAdmin", 11 | process.env.FROUTER_PROXY 12 | ); 13 | await proxyAdmin.upgradeAndCall(process.env.FROUTER, contract.target, "0x"); 14 | console.log("Upgraded FROUTER"); 15 | } catch (e) { 16 | console.log(e); 17 | } 18 | })(); 19 | -------------------------------------------------------------------------------- /test/deprecated/genesisDAO.js.old: -------------------------------------------------------------------------------- 1 | const { parseEther } = require("ethers/utils"); 2 | const { keccak256 } = require("ethers/crypto"); 3 | const { expect } = require("chai"); 4 | const { 5 | loadFixture, 6 | mine, 7 | } = require("@nomicfoundation/hardhat-toolbox/network-helpers"); 8 | 9 | const getMintCallData = (token, to, amount) => { 10 | return token.interface.encodeFunctionData("mint", [to, amount]); 11 | }; 12 | 13 | describe("GenesisDAO", function () { 14 | const PROPOSAL_THRESHOLD = parseEther("100000000"); 15 | const QUORUM = parseEther("10000"); 16 | const VOTING_PERIOD = parseInt(process.env.PROTOCOL_VOTING_PERIOD); 17 | 18 | before(async function () { 19 | const signers = await ethers.getSigners(); 20 | this.accounts = signers.map((signer) => signer.address); 21 | this.signers = signers; 22 | }); 23 | 24 | async function deployGovFixture() { 25 | const [deployer] = await ethers.getSigners(); 26 | const veToken = await ethers.deployContract( 27 | "veVirtualToken", 28 | [deployer.address], 29 | {} 30 | ); 31 | await veToken.waitForDeployment(); 32 | 33 | const dao = await ethers.deployContract( 34 | "VirtualGenesisDAO", 35 | [veToken.target, 0, 100, 0], 36 | {} 37 | ); 38 | await dao.waitForDeployment(); 39 | await dao.grantRole("0xd8aa0f3194971a2a116679f7c2090f6939c8d4e01a2a8d7e41d55e5351469e63", deployer.address); // EXECUTOR_ROLE 40 | 41 | const demoToken = await ethers.deployContract( 42 | "BMWToken", 43 | [deployer.address], 44 | {} 45 | ); 46 | await demoToken.waitForDeployment(); 47 | 48 | return { veToken, dao, demoToken }; 49 | } 50 | 51 | async function createProposalFixture() { 52 | const [deployer, voter, recipient] = await ethers.getSigners(); 53 | const { veToken, dao, demoToken } = await loadFixture(deployGovFixture); 54 | // Proposal creator will need 100m tokens 55 | await veToken.oracleTransfer( 56 | [ethers.ZeroAddress], 57 | [deployer.address], 58 | [PROPOSAL_THRESHOLD] 59 | ); 60 | await veToken.delegate(deployer.address); 61 | 62 | await veToken.oracleTransfer( 63 | [ethers.ZeroAddress], 64 | [voter.address], 65 | [QUORUM] 66 | ); 67 | await veToken.connect(voter).delegate(voter.address); 68 | 69 | const tx = await dao.propose( 70 | [demoToken.target], 71 | [0], 72 | [getMintCallData(demoToken, recipient.address, parseEther("100"))], 73 | "Give grant" 74 | ); 75 | 76 | const filter = dao.filters.ProposalCreated; 77 | const events = await dao.queryFilter(filter, -1); 78 | const event = events[0]; 79 | const proposalId = event.args[0]; 80 | return { veToken, dao, demoToken, proposalId }; 81 | } 82 | 83 | it("should allow early execution", async function () { 84 | const { veToken, dao, demoToken, proposalId } = await loadFixture( 85 | createProposalFixture 86 | ); 87 | const tx = await dao.connect(this.signers[1]).castVote(proposalId, 1); 88 | // Ensure proposal is still in ACTIVE state 89 | expect(await dao.state(proposalId)).to.be.equal(1n); 90 | 91 | // // Try to execute proposal 92 | expect(await demoToken.balanceOf(this.accounts[2])).to.equal( 93 | parseEther("0") 94 | ); 95 | 96 | await expect(dao.earlyExecute(proposalId)).to.be.fulfilled; 97 | 98 | expect(await demoToken.balanceOf(this.accounts[2])).to.equal( 99 | parseEther("100") 100 | ); 101 | }); 102 | 103 | it("should not allow double executions", async function () { 104 | const { veToken, dao, demoToken, proposalId } = await loadFixture( 105 | createProposalFixture 106 | ); 107 | const tx = await dao.connect(this.signers[1]).castVote(proposalId, 1); 108 | // Ensure proposal is still in ACTIVE state 109 | expect(await dao.state(proposalId)).to.be.equal(1n); 110 | 111 | // Try to execute proposal 112 | expect(await demoToken.balanceOf(this.accounts[2])).to.equal( 113 | parseEther("0") 114 | ); 115 | 116 | await expect(dao.earlyExecute(proposalId)).to.be.fulfilled; 117 | 118 | await mine(10); 119 | 120 | await expect(dao.earlyExecute(proposalId)).to.be.reverted; 121 | }); 122 | 123 | it("should not allow double executions", async function () { 124 | const { veToken, dao, demoToken, proposalId } = await loadFixture( 125 | createProposalFixture 126 | ); 127 | const tx = await dao.connect(this.signers[1]).castVote(proposalId, 1); 128 | // Ensure proposal is still in ACTIVE state 129 | expect(await dao.state(proposalId)).to.be.equal(1n); 130 | 131 | // Try to execute proposal 132 | expect(await demoToken.balanceOf(this.accounts[2])).to.equal( 133 | parseEther("0") 134 | ); 135 | 136 | await expect(dao.earlyExecute(proposalId)).to.be.fulfilled; 137 | 138 | await mine(100); 139 | 140 | await expect(dao.execute(proposalId)).to.be.reverted; 141 | 142 | expect(await demoToken.balanceOf(this.accounts[2])).to.equal( 143 | parseEther("100") 144 | ); 145 | }); 146 | 147 | it("should allow normal execution", async function () { 148 | const { veToken, dao, demoToken, proposalId } = await loadFixture( 149 | createProposalFixture 150 | ); 151 | const tx = await dao.connect(this.signers[1]).castVote(proposalId, 1); 152 | // Ensure proposal is still in ACTIVE state 153 | expect(await dao.state(proposalId)).to.be.equal(1n); 154 | 155 | // Try to execute proposal 156 | expect(await demoToken.balanceOf(this.accounts[2])).to.equal( 157 | parseEther("0") 158 | ); 159 | 160 | await mine(90); 161 | await expect(dao.execute(proposalId)).to.be.reverted; 162 | await mine(10); 163 | await expect(dao.execute(proposalId)).to.be.fulfilled; 164 | 165 | expect(await demoToken.balanceOf(this.accounts[2])).to.equal( 166 | parseEther("100") 167 | ); 168 | }); 169 | }); 170 | -------------------------------------------------------------------------------- /test/deprecated/staking.js.old: -------------------------------------------------------------------------------- 1 | /* 2 | Test delegation with history 3 | */ 4 | const { parseEther, toBeHex } = require("ethers/utils"); 5 | const { expect } = require("chai"); 6 | const { 7 | loadFixture, 8 | mine, 9 | } = require("@nomicfoundation/hardhat-toolbox/network-helpers"); 10 | 11 | const getExecuteCallData = (factory, proposalId) => { 12 | return factory.interface.encodeFunctionData("executeApplication", [ 13 | proposalId, 14 | ]); 15 | }; 16 | 17 | describe("Staking", function () { 18 | async function deployBaseContracts() { 19 | const [deployer] = await ethers.getSigners(); 20 | const token = await ethers.deployContract( 21 | "VirtualToken", 22 | [parseEther("100"), deployer.address], 23 | {} 24 | ); 25 | await token.waitForDeployment(); 26 | 27 | const stakingArgs = require("../scripts/arguments/stakingArguments"); 28 | stakingArgs[2] = token.target; 29 | 30 | const staking = await ethers.deployContract( 31 | "TimeLockStaking", 32 | stakingArgs, 33 | {} 34 | ); 35 | await staking.waitForDeployment(); 36 | 37 | return { token, staking }; 38 | } 39 | 40 | before(async function () { 41 | const signers = await ethers.getSigners(); 42 | this.accounts = signers.map((signer) => signer.address); 43 | this.signers = signers; 44 | }); 45 | 46 | it("should give correct bonus for 6months", async function () { 47 | const { token, staking } = await loadFixture(deployBaseContracts); 48 | 49 | const [account1, account2] = this.accounts; 50 | await token.approve(staking.target, parseEther("100")); 51 | const oneYear = 86400 * 365; 52 | 53 | await staking.deposit(parseEther("100"), Math.floor(oneYear / 2), account2); 54 | const balance = await staking.balanceOf(account2); 55 | expect(balance).to.equal(parseEther("150")); 56 | }); 57 | 58 | it("should give correct bonus for year 1", async function () { 59 | const { token, staking } = await loadFixture(deployBaseContracts); 60 | 61 | const [account1, account2] = this.accounts; 62 | await token.approve(staking.target, parseEther("100")); 63 | const oneYear = 86400 * 365; 64 | 65 | await staking.deposit(parseEther("100"), oneYear, account2); 66 | const balance = await staking.balanceOf(account2); 67 | expect(balance).to.equal(parseEther("200")); 68 | }); 69 | 70 | it("should give correct bonus for year 2", async function () { 71 | const { token, staking } = await loadFixture(deployBaseContracts); 72 | 73 | const [account1, account2] = this.accounts; 74 | await token.approve(staking.target, parseEther("100")); 75 | const oneYear = 86400 * 365; 76 | 77 | await staking.deposit(parseEther("100"), oneYear * 2, account2); 78 | const balance = await staking.balanceOf(account2); 79 | expect(balance).to.equal(parseEther("400")); 80 | }); 81 | 82 | it("should give correct bonus for year 3", async function () { 83 | const { token, staking } = await loadFixture(deployBaseContracts); 84 | 85 | const [account1, account2] = this.accounts; 86 | await token.approve(staking.target, parseEther("100")); 87 | const oneYear = 86400 * 365; 88 | 89 | await staking.deposit(parseEther("100"), oneYear * 3, account2); 90 | const balance = await staking.balanceOf(account2); 91 | expect(balance).to.equal(parseEther("800")); 92 | }); 93 | 94 | it("should give correct bonus for year 3.5", async function () { 95 | const { token, staking } = await loadFixture(deployBaseContracts); 96 | 97 | const [account1, account2] = this.accounts; 98 | await token.approve(staking.target, parseEther("100")); 99 | const oneYear = 86400 * 365; 100 | 101 | await staking.deposit(parseEther("100"), Math.floor(oneYear * 3.5), account2); 102 | const balance = await staking.balanceOf(account2); 103 | expect(balance).to.equal(parseEther("1200")); 104 | }); 105 | 106 | it("should give correct bonus for year 4", async function () { 107 | const { token, staking } = await loadFixture(deployBaseContracts); 108 | 109 | const [account1, account2] = this.accounts; 110 | await token.approve(staking.target, parseEther("100")); 111 | const oneYear = 86400 * 365; 112 | 113 | await staking.deposit(parseEther("100"), oneYear * 4, account2); 114 | const balance = await staking.balanceOf(account2); 115 | expect(balance).to.equal(parseEther("1600")); 116 | }); 117 | 118 | it("should allow adjustDeposits", async function() { 119 | const { token, staking } = await loadFixture(deployBaseContracts); 120 | const [deployer, admin] = this.signers 121 | await expect(staking.adjustAdminUnlock()).to.be.reverted; 122 | await expect(staking.connect(admin).adjustAdminUnlock()).to.be.reverted; 123 | 124 | await staking.grantRole(await staking.GOV_ROLE(), admin.address); 125 | await expect(staking.connect(admin).adjustAdminUnlock()).to.be.fulfilled; 126 | }) 127 | }); 128 | -------------------------------------------------------------------------------- /test/elo.js: -------------------------------------------------------------------------------- 1 | /* 2 | Test delegation with history 3 | */ 4 | const { expect } = require("chai"); 5 | const { ethers } = require("hardhat"); 6 | 7 | describe("Elo Rating", function () { 8 | before(async function () { 9 | const signers = await ethers.getSigners(); 10 | this.accounts = signers.map((signer) => signer.address); 11 | this.signers = signers; 12 | }); 13 | 14 | it("should calculate correct elo", async function () { 15 | const [deployer] = await ethers.getSigners(); 16 | const Contract = await ethers.getContractFactory("EloCalculator"); 17 | const calculator = await upgrades.deployProxy(Contract, [deployer.address]); 18 | 19 | const res = await calculator.battleElo(100, [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]); 20 | expect(res).to.be.equal(315n); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /test/genesis/const.js: -------------------------------------------------------------------------------- 1 | // test/genesis/const.js 2 | 3 | // Genesis state error messages 4 | const ERR_NOT_STARTED = "Genesis not started yet"; 5 | const ERR_ALREADY_STARTED = "Genesis already started"; 6 | const ERR_NOT_ENDED = "Genesis not ended yet"; 7 | const ERR_ALREADY_ENDED = "Genesis already ended"; 8 | const ERR_ALREADY_FAILED = "Genesis already failed"; 9 | const ERR_ALREADY_CANCELLED = "Genesis already cancelled"; 10 | 11 | // Time validation error messages 12 | const ERR_START_TIME_FUTURE = "Start time must be in the future"; 13 | const ERR_END_AFTER_START = "End time must be after start time"; 14 | 15 | // Token related error messages 16 | const ERR_TOKEN_LAUNCHED = "Agent token already launched"; 17 | const ERR_TOKEN_NOT_LAUNCHED = "Agent token not launched"; 18 | 19 | // Balance related error messages 20 | const ERR_INSUFFICIENT_BALANCE = "Insufficient Virtual Token balance"; 21 | const ERR_INSUFFICIENT_ALLOWANCE = "Insufficient Virtual Token allowance"; 22 | const ERR_INSUFFICIENT_COMMITTED = "Insufficient Virtual Token committed"; 23 | const ERR_NO_TOKENS_TO_CLAIM = "No tokens to claim"; 24 | 25 | // Other validation error messages 26 | const ERR_INVALID_TOKEN_ADDRESS = "Invalid token address"; 27 | const ERR_NO_TOKEN_TO_WITHDRAW = "No token left to withdraw"; 28 | const ERR_INDEX_OUT_OF_BOUNDS = "Start index out of bounds"; 29 | 30 | module.exports = { 31 | ERR_NOT_STARTED, 32 | ERR_ALREADY_STARTED, 33 | ERR_NOT_ENDED, 34 | ERR_ALREADY_ENDED, 35 | ERR_ALREADY_FAILED, 36 | ERR_ALREADY_CANCELLED, 37 | ERR_START_TIME_FUTURE, 38 | ERR_END_AFTER_START, 39 | ERR_TOKEN_LAUNCHED, 40 | ERR_TOKEN_NOT_LAUNCHED, 41 | ERR_INSUFFICIENT_BALANCE, 42 | ERR_INSUFFICIENT_ALLOWANCE, 43 | ERR_INSUFFICIENT_COMMITTED, 44 | ERR_NO_TOKENS_TO_CLAIM, 45 | ERR_INVALID_TOKEN_ADDRESS, 46 | ERR_NO_TOKEN_TO_WITHDRAW, 47 | ERR_INDEX_OUT_OF_BOUNDS 48 | }; -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2020", 4 | "module": "commonjs", 5 | "esModuleInterop": true, 6 | "forceConsistentCasingInFileNames": true, 7 | "strict": true, 8 | "skipLibCheck": true, 9 | "resolveJsonModule": true 10 | }, 11 | "exclude": ["dist", "node_modules"], 12 | "include": ["./test", "./src", "./scripts"], 13 | "files": ["./hardhat.config.ts"] 14 | } 15 | --------------------------------------------------------------------------------