Skip to content

Feat/reward v2 #24

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Jun 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,724 changes: 1,722 additions & 2 deletions .openzeppelin/base-sepolia.json

Large diffs are not rendered by default.

433 changes: 408 additions & 25 deletions contracts/AgentRewardV2.sol

Large diffs are not rendered by default.

92 changes: 24 additions & 68 deletions contracts/IAgentReward.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,93 +2,49 @@
pragma solidity ^0.8.20;

interface IAgentReward {
struct MainReward {
uint32 blockNumber;
struct Reward {
uint256 blockNumber;
uint256 amount;
uint256 agentCount;
uint256 totalStaked;
uint256[] lpValues;
uint256[] virtualIds;
}

// Virtual specific reward, the amount will be shared between validator pool and contributor pool
// Validator pool will be shared by validators and stakers
// Contributor pool will be shared by contribution NFT holders
struct Reward {
uint48 id;
uint32 mainIndex;
uint256 totalStaked;
// Agent specific reward, the amount will be shared between stakers and validators
struct AgentReward {
uint256 id;
uint256 rewardIndex;
uint256 stakerAmount;
uint256 validatorAmount;
uint256 contributorAmount;
uint256 coreAmount; // Rewards per core
uint256 totalProposals;
uint256 totalStaked;
}

struct Claim {
uint256 totalClaimed;
uint32 rewardCount; // Track number of reward blocks claimed to avoid reclaiming
uint256 rewardCount; // Track number of reward blocks claimed to avoid reclaiming
}

struct ServiceReward {
uint256 impact;
uint256 amount;
uint256 parentAmount;
uint256 totalClaimed;
uint256 totalClaimedParent;
}
event NewReward(uint256 pos, uint256[] virtualIds);

event NewMainReward(
uint32 indexed pos,
uint256 amount,
uint256 agentCount,
uint256 totalStaked
);
event NewAgentReward(uint256 indexed virtualId, uint256 id);

event RewardSettingsUpdated(
uint16 protocolShares,
uint16 contributorShares,
uint16 stakerShares,
uint16 parentShares,
uint256 stakeThreshold
);
event RewardSettingsUpdated(uint16 protocolShares, uint16 stakerShares);

event RefContractsUpdated(
address rewardToken,
address agentNft,
address contributionNft,
address serviceNft
);

event StakeThresholdUpdated(uint256 threshold);

event ParentSharesUpdated(uint256 shares);
event RefContractsUpdated(address rewardToken, address agentNft);

event StakerRewardClaimed(
uint256 virtualId,
uint256 amount,
address staker
uint256 indexed virtualId,
address indexed staker,
uint256 numRewards,
uint256 amount
);

event ValidatorRewardClaimed(
uint256 virtualId,
uint256 amount,
address validator
);

event ServiceRewardsClaimed(
uint256 nftId,
address account,
uint256 total,
uint256 childrenAmount
uint256 indexed virtualId,
address indexed validator,
uint256 amount
);

event NewAgentReward(
uint32 mainIndex,
uint256 virtualId,
uint256 validatorAmount,
uint256 contributorAmount,
uint256 coreAmount
);

event DatasetRewardsClaimed(uint256 nftId, address account, uint256 total);


error ERC5805FutureLookup(uint256 timepoint, uint32 clock);

error NotGovError();
Expand Down
7 changes: 2 additions & 5 deletions contracts/libs/RewardSettingsCheckpoints.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,7 @@ library RewardSettingsCheckpoints {

struct RewardSettings {
uint16 protocolShares;
uint16 contributorShares;
uint16 stakerShares;
uint16 parentShares; // Optional rewards for contribution's parent
uint256 stakeThreshold; // Each VIRTUAL will require minimum amount of staked tokens to be considered for rewards
}

struct Checkpoint {
Expand Down Expand Up @@ -62,7 +59,7 @@ library RewardSettingsCheckpoints {
uint256 pos = self._checkpoints.length;
return
pos == 0
? RewardSettings(0, 0, 0, 0, 0)
? RewardSettings(0, 0)
: self._checkpoints[pos - 1]._value;
}

Expand All @@ -88,7 +85,7 @@ library RewardSettingsCheckpoints {

return
pos == 0
? RewardSettings(0, 0, 0, 0, 0)
? RewardSettings(0, 0)
: self._checkpoints[pos - 1]._value;
}

Expand Down
24 changes: 18 additions & 6 deletions contracts/token/Minter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ contract Minter is IMinter, Ownable {

uint256 public ipShare; // Share for IP holder
uint256 public dataShare; // Share for Dataset provider
uint256 public impactMultiplier;

uint256 public constant DENOM = 10000;

mapping(uint256 => bool) _mintedNfts;

Expand All @@ -38,6 +41,8 @@ contract Minter is IMinter, Ownable {
address contributionAddress,
address agentAddress,
uint256 _ipShare,
uint256 _dataShare,
uint256 _impactMultiplier,
address _ipVault,
address _agentFactory,
address initialOwner
Expand All @@ -46,6 +51,8 @@ contract Minter is IMinter, Ownable {
contributionNft = contributionAddress;
agentNft = agentAddress;
ipShare = _ipShare;
dataShare = _dataShare;
impactMultiplier = _impactMultiplier;
ipVault = _ipVault;
agentFactory = _agentFactory;
}
Expand Down Expand Up @@ -79,6 +86,10 @@ contract Minter is IMinter, Ownable {
agentFactory = _factory;
}

function setImpactMultiplier(uint256 _multiplier) public onlyOwner {
impactMultiplier = _multiplier;
}

function mint(uint256 nftId) public noReentrant {
// Mint configuration:
// 1. ELO impact amount, to be shared between model and dataset owner
Expand All @@ -95,15 +106,16 @@ contract Minter is IMinter, Ownable {
_mintedNfts[nftId] = true;

address tokenAddress = IAgentNft(agentNft).virtualInfo(agentId).token;
uint256 datasetId = IContributionNft(contributionNft).getDatasetId(
nftId
);
uint256 amount = (IServiceNft(serviceNft).getImpact(nftId) * 10 ** 18);
uint256 ipAmount = (amount * ipShare) / 10000;
IContributionNft contribution = IContributionNft(contributionNft);
require(contribution.isModel(nftId), "Not a model contribution");

uint256 datasetId = contribution.getDatasetId(nftId);
uint256 amount = (IServiceNft(serviceNft).getImpact(nftId) * impactMultiplier * 10 ** 18) / DENOM;
uint256 ipAmount = (amount * ipShare) / DENOM;
uint256 dataAmount = 0;

if (datasetId != 0) {
dataAmount = (amount * ipShare) / 10000;
dataAmount = (amount * dataShare) / DENOM;
amount = amount - dataAmount;
}

Expand Down
7 changes: 7 additions & 0 deletions contracts/virtualPersona/AgentDAO.sol
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ contract AgentDAO is

error ERC5805FutureLookup(uint256 timepoint, uint48 clock);

uint256 private _totalScore;

constructor() {
_disableInitializers();
}
Expand Down Expand Up @@ -164,6 +166,7 @@ contract AgentDAO is
);

if (!votedPreviously && hasVoted(proposalId, account)) {
++_totalScore;
_scores[account].push(
SafeCast.toUint48(block.number),
SafeCast.toUint208(scoreOf(account)) + 1
Expand Down Expand Up @@ -238,4 +241,8 @@ contract AgentDAO is
}
return currentState;
}

function totalScore() public view override returns (uint256) {
return _totalScore;
}
}
23 changes: 17 additions & 6 deletions contracts/virtualPersona/AgentFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -94,11 +94,12 @@ contract AgentFactoryV2 is IAgentFactory, Initializable, AccessControl {
///////////////////////////////////////////////////////////////
// V2 Storage
///////////////////////////////////////////////////////////////
address[] public allTradingTokens;
address private _uniswapRouter;
address public veTokenImplementation;
address private _minter;
address private _tokenAdmin;

address[] public allTradingTokens;
address public veTokenImplementation;
address public defaultDelegatee;

// Default agent token params
Expand Down Expand Up @@ -223,7 +224,7 @@ contract AgentFactoryV2 is IAgentFactory, Initializable, AccessControl {
);
}

function executeApplication(uint256 id) public noReentrant {
function executeApplication(uint256 id, bool canStake) public noReentrant {
// This will bootstrap an Agent with following components:
// C1: Agent Token
// C2: LP Pool + Initial liquidity
Expand Down Expand Up @@ -270,7 +271,8 @@ contract AgentFactoryV2 is IAgentFactory, Initializable, AccessControl {
string.concat("Staked ", application.name),
string.concat("s", application.symbol),
lp,
application.proposer
application.proposer,
canStake
);

// C4
Expand Down Expand Up @@ -347,6 +349,7 @@ contract AgentFactoryV2 is IAgentFactory, Initializable, AccessControl {
string memory symbol,
uint256 initialSupply
) internal returns (address instance) {

instance = Clones.clone(tokenImplementation);
IAgentToken(instance).initialize(
[_tokenAdmin, _uniswapRouter, assetToken],
Expand All @@ -364,7 +367,8 @@ contract AgentFactoryV2 is IAgentFactory, Initializable, AccessControl {
string memory name,
string memory symbol,
address stakingAsset,
address founder
address founder,
bool canStake
) internal returns (address instance) {
instance = Clones.clone(veTokenImplementation);
IAgentVeToken(instance).initialize(
Expand All @@ -373,7 +377,8 @@ contract AgentFactoryV2 is IAgentFactory, Initializable, AccessControl {
founder,
stakingAsset,
block.timestamp + maturityDuration,
address(nft)
address(nft),
canStake
);

allTokens.push(instance);
Expand Down Expand Up @@ -468,4 +473,10 @@ contract AgentFactoryV2 is IAgentFactory, Initializable, AccessControl {
) public onlyRole(DEFAULT_ADMIN_ROLE) {
defaultDelegatee = newDelegatee;
}

function setAssetToken(
address newToken
) public onlyRole(DEFAULT_ADMIN_ROLE) {
assetToken = newToken;
}
}
1 change: 0 additions & 1 deletion contracts/virtualPersona/AgentNftV2.sol
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,6 @@ contract AgentNftV2 is

_stakingTokenToVirtualId[address(daoToken)] = virtualId;
_addValidator(virtualId, founder);
_initValidatorScore(virtualId, founder);
return virtualId;
}

Expand Down
38 changes: 25 additions & 13 deletions contracts/virtualPersona/AgentVeToken.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ contract AgentVeToken is IAgentVeToken, ERC20Upgradeable, ERC20Votes {
address public agentNft;
uint256 public matureAt; // The timestamp when the founder can withdraw the tokens
bool public canStake; // To control private/public agent mode
uint256 private initialLock; // Initial locked amount
uint256 public initialLock; // Initial locked amount

constructor() {
_disableInitializers();
Expand All @@ -37,20 +37,22 @@ contract AgentVeToken is IAgentVeToken, ERC20Upgradeable, ERC20Votes {
}

function initialize(
string memory name,
string memory symbol,
address founder_,
address assetToken_,
uint256 matureAt_,
address agentNft_
string memory _name,
string memory _symbol,
address _founder,
address _assetToken,
uint256 _matureAt,
address _agentNft,
bool _canStake
) external initializer {
__ERC20_init(name, symbol);
__ERC20_init(_name, _symbol);
__ERC20Votes_init();

founder = founder_;
matureAt = matureAt_;
assetToken = assetToken_;
agentNft = agentNft_;
founder = _founder;
matureAt = _matureAt;
assetToken = _assetToken;
agentNft = _agentNft;
canStake = _canStake;
}

// Stakers have to stake their tokens and delegate to a validator
Expand All @@ -62,6 +64,14 @@ contract AgentVeToken is IAgentVeToken, ERC20Upgradeable, ERC20Votes {

address sender = _msgSender();
require(amount > 0, "Cannot stake 0");
require(
IERC20(assetToken).balanceOf(sender) >= amount,
"Insufficient asset token balance"
);
require(
IERC20(assetToken).allowance(sender, address(this)) >= amount,
"Insufficient asset token allowance"
);

if (totalSupply() == 0) {
initialLock = amount;
Expand Down Expand Up @@ -98,7 +108,9 @@ contract AgentVeToken is IAgentVeToken, ERC20Upgradeable, ERC20Votes {
address sender = _msgSender();
require(balanceOf(sender) >= amount, "Insufficient balance");

if ((sender == founder) && ((balanceOf(sender) - amount) < initialLock)) {
if (
(sender == founder) && ((balanceOf(sender) - amount) < initialLock)
) {
require(block.timestamp >= matureAt, "Not mature yet");
}

Expand Down
2 changes: 2 additions & 0 deletions contracts/virtualPersona/IAgentDAO.sol
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ interface IAgentDAO {

function scoreOf(address account) external view returns (uint256);

function totalScore() external view returns (uint256);

function getPastScore(
address account,
uint256 timepoint
Expand Down
Loading