Skip to content

Commit

Permalink
Dev (#206)
Browse files Browse the repository at this point in the history
* amoy settings

* try execute in lib

* tryExecute

* refactor

* amoy settings (#198)

* amoy settings (#198) (#200)

* Network/optimism (#202)

* env parameters

* removed duplicated network

* Dev (#203) (#204)

* amoy settings (#198)

* Network/optimism (#202)

* env parameters

* removed duplicated network

* gas amount (#205)

* 20k gas (#207)

* revert just in case (#208)

* gas 30k (#209)

* Networks/base sepolia (#212)

* initial settings

* verify parameters and token addresses

* final properties

* Fixed quorum fishing attack (#210)

* fixed solidity todo tests

* regression test

* naming

* fix tests coverage (#214)

---------

Co-authored-by: Dmitry Redkin <tanatonaut@gmail.com>
Co-authored-by: todesstille <87335281+todesstille@users.noreply.github.com>

* Feature/token preallocation (#213)

* allocation added

* refactored

* tests

* environment proxy and tests

* govpool preallocation

* fixes

* added allocator and payee roles

* metadata

* view functions

* added auxiliary contracts deploy

* dependand

* tx.origin

* unlock in multiplier

* function annotations

* fixes

* fixes

* Feature/token factory (#215)

* predeploy token

* handle invalid deploy

* integrated with allocator

* token allocator integration

* Feature/gov tokens (#216)

* burnable pausable

* capped

* tokens

* removed old tests

* fixes (#217)

* revards 1/100

---------

Co-authored-by: Artem Chystiakov <47551140+Arvolear@users.noreply.github.com>
  • Loading branch information
todesstille and Arvolear authored Oct 3, 2024
1 parent 3b14481 commit 15c744d
Show file tree
Hide file tree
Showing 63 changed files with 22,483 additions and 751 deletions.
6 changes: 5 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
PRIVATE_KEY = "YOUR PRIVATE KEY"
AUXILIARY_KEY = "AUXILIARY PRIVATE KEY"
INFURA_KEY = "INFURA PROJECT ID"
ETHERSCAN_KEY = "ETHERSCAN API KEY"
BSCSCAN_KEY = "BSCSCAN API KEY"
POLYGONSCAN_KEY = "POLYGONSCAN API KEY"
OPTIMISM_KEY = "OPTIMISM SCAN API KEY"
BASE_KEY = "BASESCAN API KEY"
COINMARKETCAP_KEY = "COINMARKETCAP API KEY"

# Available targets: 'ethers-v5', 'truffle-v5' and 'web3-v1'
# By default 'ethers-v5'
TYPECHAIN_TARGET = "TYPECHAIN TARGET"

# Environment
ENVIRONMENT = "PROD|STAGE|DEV|STAGE_SEPOLIA|DEV_SEPOLIA"
ENVIRONMENT = "PROD|STAGE|DEV|STAGE_SEPOLIA|DEV_SEPOLIA|DEV_AMOY|DEV_OPTIMISM|DEV_BASE"
12 changes: 12 additions & 0 deletions contracts/core/ContractsRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ contract ContractsRegistry is IContractsRegistry, MultiOwnableContractsRegistry,
string public constant DEXE_EXPERT_NFT_NAME = "DEXE_EXPERT_NFT";

string public constant PRICE_FEED_NAME = "PRICE_FEED";
string public constant TOKEN_ALLOCATOR_NAME = "TOKEN_ALLOCATOR";

string public constant TREASURY_NAME = "TREASURY";

Expand Down Expand Up @@ -60,6 +61,13 @@ contract ContractsRegistry is IContractsRegistry, MultiOwnableContractsRegistry,
}
}

function injectDependenciesBatch(string[] calldata names_) external onlyOwner {
uint256 length = names_.length;
for (uint256 i = 0; i < length; i++) {
_injectDependencies(names_[i]);
}
}

function protectContractFunctions(
string calldata contractName,
bytes4[] calldata selectors
Expand Down Expand Up @@ -102,6 +110,10 @@ contract ContractsRegistry is IContractsRegistry, MultiOwnableContractsRegistry,
return getContract(PRICE_FEED_NAME);
}

function getTokenAllocatorContract() external view override returns (address) {
return getContract(TOKEN_ALLOCATOR_NAME);
}

function getTreasuryContract() external view override returns (address) {
return getContract(TREASURY_NAME);
}
Expand Down
285 changes: 285 additions & 0 deletions contracts/core/TokenAllocator.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,285 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol";
import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import "@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol";

import "@solarity/solidity-lib/contracts-registry/AbstractDependant.sol";
import "@solarity/solidity-lib/access-control/MultiOwnable.sol";

import "../interfaces/core/ITokenAllocator.sol";
import "../interfaces/core/IContractsRegistry.sol";

contract TokenAllocator is ITokenAllocator, AbstractDependant, MultiOwnable, UUPSUpgradeable {
using MerkleProof for *;
using SafeERC20 for IERC20;
using EnumerableSet for *;

uint256 public lastAllocationId;
mapping(address => EnumerableSet.AddressSet) internal _tokensByAllocator;
mapping(address => EnumerableSet.AddressSet) internal _allocatorsByToken;
mapping(address => mapping(address => EnumerableSet.UintSet)) internal _allocations;
mapping(uint256 => AllocationData) internal _allocationInfos;

IPoolFactory internal _poolFactory;

event AllocationCreated(
uint256 id,
address allocator,
address token,
uint256 amount,
bytes32 merkleRoot,
string descriptionUrl
);
event AllocationClosed(uint256 id, address token, uint256 amountReturned);
event TokenClaimed(
address allocator,
address token,
bytes32 merkleRoot,
address user,
uint256 amount
);

modifier withCorrectId(uint256 id) {
require(id <= lastAllocationId, "TA: invalid allocation id");
_;
}

function __TokenAllocator_init() external initializer {
__MultiOwnable_init();
}

function setDependencies(
address contractsRegistry,
bytes memory data_
) public override(AbstractDependant, ITokenAllocator) dependant {
IContractsRegistry registry = IContractsRegistry(contractsRegistry);

_poolFactory = IPoolFactory(registry.getPoolFactoryContract());
}

function createAllocation(
address token,
uint256 amount,
bytes32 merkleRoot,
string calldata descriptionURL
) external {
_createAllocation(msg.sender, token, amount, merkleRoot, descriptionURL);

IERC20(token).safeTransferFrom(msg.sender, address(this), amount);
}

function allocateAndDeployGovPool(
bytes32 merkleRoot,
string calldata descriptionURL,
IPoolFactory.GovPoolDeployParams calldata parameters
) external {
(address allocator, address token, uint256 amount) = _retrieveAllocationData(parameters);

_createAllocation(allocator, token, amount, merkleRoot, descriptionURL);

_poolFactory.deployGovPool(parameters);
}

function allocateFromFactory(
address token,
uint256 amount,
address pool,
bytes32 merkleRoot,
string calldata descriptionURL
) external {
require(address(_poolFactory) == msg.sender, "TA: Not factory");

_createAllocation(pool, token, amount, merkleRoot, descriptionURL);

IERC20(token).safeTransferFrom(msg.sender, address(this), amount);
}

function closeAllocation(uint256 id) external withCorrectId(id) {
AllocationData storage allocation = _allocationInfos[id];
address allocator = allocation.allocator;

require(allocator == msg.sender, "TA: wrong allocator");
require(!allocation.isClosed, "TA: already closed");

allocation.isClosed = true;

address token = allocation.token;
_allocations[allocator][token].remove(id);

uint256 balance = allocation.balance;
if (balance > 0) {
IERC20(token).safeTransfer(allocator, balance);
}

allocation.balance = 0;

emit AllocationClosed(id, token, balance);
}

function claim(
uint256 id,
uint256 amount,
bytes32[] calldata proof
) external withCorrectId(id) {
AllocationData storage allocationInfo = _allocationInfos[id];

require(!allocationInfo.isClosed, "TA: allocation is closed");

address user = msg.sender;
require(allocationInfo.claimed.add(user), "TA: already claimed");

require(allocationInfo.balance >= amount, "TA: insufficient funds");
allocationInfo.balance -= amount;

bytes32 leaf = keccak256(bytes.concat(keccak256(abi.encode(user, amount))));
bytes32 merkleRoot = allocationInfo.merkleRoot;
require(proof.verifyCalldata(merkleRoot, leaf), "TA: Invalid proof");

address token = allocationInfo.token;
IERC20(token).safeTransfer(user, amount);

emit TokenClaimed(allocationInfo.allocator, token, merkleRoot, user, amount);
}

function getAllocations(
address allocator,
address token
) public view returns (AllocationInfoView[] memory allocations) {
uint256[] memory ids = _allocations[allocator][token].values();
allocations = _idsToAllocationInfos(ids);
}

function getAllocationsByTokenOrAllocator(
address key,
bool byToken
) external view returns (AllocationInfoView[] memory allocations) {
address token;
address allocator;
byToken ? token = key : allocator = key;

address[] memory addresses = byToken
? _allocatorsByToken[token].values()
: _tokensByAllocator[allocator].values();

uint256 allocationsLength = 0;
for (uint i = 0; i < addresses.length; i++) {
byToken ? allocator = addresses[i] : token = addresses[i];
allocationsLength += _allocations[allocator][token].length();
}

allocations = new AllocationInfoView[](allocationsLength);
uint256 index;
for (uint i = 0; i < addresses.length; i++) {
byToken ? allocator = addresses[i] : token = addresses[i];

_arraysCopy(getAllocations(allocator, token), allocations, index);
index += _allocations[allocator][token].length();
}
}

function getAllocationInfo(
uint256 id
) public view withCorrectId(id) returns (AllocationInfoView memory allocationInfo) {
AllocationData storage allocation = _allocationInfos[id];

allocationInfo = AllocationInfoView(
id,
allocation.isClosed,
allocation.allocator,
allocation.token,
allocation.balance,
allocation.merkleRoot,
allocation.descriptionURL
);
}

function isClaimed(uint256 id, address user) external view withCorrectId(id) returns (bool) {
AllocationData storage allocationInfo = _allocationInfos[id];

return allocationInfo.claimed.contains(user);
}

function _createAllocation(
address allocator,
address token,
uint256 amount,
bytes32 merkleRoot,
string calldata descriptionURL
) internal {
uint256 id = ++lastAllocationId;

require(token != address(0), "TA: Zero token address");
require(amount > 0, "TA: Zero ammount to allocate");
require(merkleRoot != bytes32(0), "TA: Zero Merkle root");

AllocationData storage allocationInfo = _allocationInfos[id];

allocationInfo.token = token;
allocationInfo.balance = amount;
allocationInfo.allocator = allocator;
allocationInfo.merkleRoot = merkleRoot;
allocationInfo.descriptionURL = descriptionURL;

_updateGlobalInfo(allocator, token, id);

emit AllocationCreated(id, allocator, token, amount, merkleRoot, descriptionURL);
}

function _updateGlobalInfo(address allocator, address token, uint256 id) internal {
_tokensByAllocator[allocator].add(token);
_allocatorsByToken[token].add(allocator);
_allocations[allocator][token].add(id);
}

function _idsToAllocationInfos(
uint256[] memory ids
) internal view returns (AllocationInfoView[] memory infos) {
uint256 length = ids.length;
infos = new AllocationInfoView[](length);

for (uint256 i = 0; i < length; i++) {
infos[i] = getAllocationInfo(ids[i]);
}
}

function _arraysCopy(
AllocationInfoView[] memory from,
AllocationInfoView[] memory to,
uint256 startingIndex
) internal pure {
for (uint256 i = 0; i < from.length; i++) {
to[startingIndex + i] = from[i];
}
}

function _retrieveAllocationData(
IPoolFactory.GovPoolDeployParams calldata parameters
) internal view returns (address allocator, address token, uint256 amount) {
IPoolFactory.GovPoolPredictedAddresses memory predictedAddresses = _poolFactory
.predictGovAddresses(tx.origin, parameters.name);
allocator = predictedAddresses.govPool;

token = parameters.userKeeperParams.tokenAddress;
require(
token == predictedAddresses.govToken,
"TA: Could preallocate only the new GovToken"
);

IERC20Gov.ConstructorParams calldata tokenParams = parameters.tokenParams;
address[] calldata users = tokenParams.users;

for (uint i = 0; i < users.length; i++) {
if (users[i] == address(this)) {
require(amount == 0, "TA: multiple allocations in GovPool params");
amount = tokenParams.amounts[i];
}
}

require(amount != 0, "TA: no allocation in GovPool params");
}

function _authorizeUpgrade(address newImplementation) internal override onlyOwner {}
}
2 changes: 1 addition & 1 deletion contracts/core/network-properties/BSCProperties.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import "./NetworkProperties.sol";
contract BSCProperties is NetworkProperties {
uint256 private constant BNB_SUPPLY = 150_000_000 * 10 ** 18;

function getNativeSupply() external view override returns (uint256) {
function getNativeSupply() external pure override returns (uint256) {
return BNB_SUPPLY;
}
}
2 changes: 1 addition & 1 deletion contracts/core/network-properties/DevProperties.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import "./NetworkProperties.sol";
contract DevProperties is NetworkProperties {
uint256 private constant BNB_SUPPLY = 100 * 10 ** 18;

function getNativeSupply() external view override returns (uint256) {
function getNativeSupply() external pure override returns (uint256) {
return BNB_SUPPLY;
}
}
2 changes: 1 addition & 1 deletion contracts/core/network-properties/ETHProperties.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import "./NetworkProperties.sol";
contract ETHProperties is NetworkProperties {
uint256 private constant ETH_SUPPLY = 120_000_000 * 10 ** 18;

function getNativeSupply() external view override returns (uint256) {
function getNativeSupply() external pure override returns (uint256) {
return ETH_SUPPLY;
}
}
Loading

0 comments on commit 15c744d

Please sign in to comment.