-
Notifications
You must be signed in to change notification settings - Fork 1
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
Voting Multipliers #100
base: dev
Are you sure you want to change the base?
Voting Multipliers #100
Changes from all commits
47bdbff
0aa29f4
2534725
bc9abf8
12fa7ea
faf437f
8c89ed0
8dd45db
e0d2527
3906049
3f06af0
0cc8661
6f95cc5
1c6c8c0
fffe13b
fc1221b
daeb1ce
cf53a4f
dce84d8
3d92685
bf324f1
5b9b7b3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.22; | ||
|
||
import {Script} from "forge-std/Script.sol"; | ||
import {NFTMultiplier} from "src/multipliers/NFTMultiplier.sol"; | ||
import {TransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; | ||
import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; | ||
import {console} from "forge-std/console.sol"; | ||
|
||
contract DeployNFTMultiplier is Script { | ||
function run() external { | ||
uint256 deployerPrivateKey; | ||
address nftContractAddress; | ||
uint256 initialMultiplyingFactor; | ||
uint256 validUntilBlock; | ||
|
||
string memory configPath = "deploy_config.json"; | ||
string memory jsonData; | ||
// Try to read the JSON file, if it doesn't exist or can't be read, catch the error | ||
try vm.readFile(configPath) returns (string memory data) { | ||
jsonData = data; | ||
} catch { | ||
console.log("Config file not found or couldn't be read. Falling back to environment variables."); | ||
jsonData = ""; | ||
} | ||
|
||
if (bytes(jsonData).length > 0) { | ||
// Read from JSON if file exists | ||
deployerPrivateKey = vm.parseJsonUint(jsonData, ".deployerPrivateKey"); | ||
nftContractAddress = vm.parseJsonAddress(jsonData, ".nftContractAddress"); | ||
initialMultiplyingFactor = vm.parseJsonUint(jsonData, ".initialMultiplyingFactor"); | ||
validUntilBlock = vm.parseJsonUint(jsonData, ".validUntilBlock"); | ||
} else { | ||
// Fall back to environment variables | ||
deployerPrivateKey = vm.envUint("PRIVATE_KEY"); | ||
nftContractAddress = vm.envAddress("NFT_CONTRACT_ADDRESS"); | ||
initialMultiplyingFactor = vm.envUint("INITIAL_MULTIPLYING_FACTOR"); | ||
validUntilBlock = vm.envUint("VALID_UNTIL_BLOCK"); | ||
} | ||
|
||
// Check if all required variables are set | ||
require(deployerPrivateKey != 0, "Deployer private key not set"); | ||
require(nftContractAddress != address(0), "NFT contract address not set"); | ||
require(initialMultiplyingFactor != 0, "Initial multiplying factor not set"); | ||
require(validUntilBlock != 0, "Valid until block not set"); | ||
|
||
vm.startBroadcast(deployerPrivateKey); | ||
|
||
NFTMultiplier implementation = new NFTMultiplier(); | ||
|
||
bytes memory initData = abi.encodeWithSelector( | ||
NFTMultiplier.initialize.selector, IERC721(nftContractAddress), initialMultiplyingFactor, validUntilBlock | ||
); | ||
|
||
TransparentUpgradeableProxy proxy = | ||
new TransparentUpgradeableProxy(address(implementation), vm.addr(deployerPrivateKey), initData); | ||
|
||
NFTMultiplier nftMultiplier = NFTMultiplier(address(proxy)); | ||
|
||
vm.stopBroadcast(); | ||
|
||
console.log("NFTMultiplier deployed at:", address(nftMultiplier)); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
{ | ||
"deployerPrivateKey": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", | ||
"nftContractAddress": "0x1234567890123456789012345678901234567890", | ||
"initialMultiplyingFactor": 100, | ||
"validUntilBlock": 1000000 | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.22; | ||
|
||
import {IVotingMultipliers, IMultiplier} from "src/interfaces/IVotingMultipliers.sol"; | ||
import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; | ||
|
||
/// @title VotingMultipliers | ||
/// @notice A contract for managing voting multipliers | ||
/// @dev Implements IVotingMultipliers interface | ||
contract VotingMultipliers is OwnableUpgradeable, IVotingMultipliers { | ||
/// @notice Array of whitelisted multiplier contracts | ||
IMultiplier[] public whitelistedMultipliers; | ||
|
||
/// @notice Calculates the total multiplier for a given user | ||
/// @param user The address of the user | ||
/// @return The total multiplier value for the user | ||
function getTotalMultipliers(address user) external view returns (uint256) { | ||
uint256 totalMultiplier = 0; | ||
for (uint256 i = 0; i < whitelistedMultipliers.length; i++) { | ||
IMultiplier multiplier = whitelistedMultipliers[i]; | ||
if (block.number <= multiplier.validUntil(user)) { | ||
totalMultiplier += multiplier.getMultiplyingFactor(user); | ||
} | ||
} | ||
return totalMultiplier; | ||
} | ||
|
||
/// @notice Adds a multiplier to the whitelist | ||
/// @param _multiplier The multiplier contract to be added | ||
function addMultiplier(IMultiplier _multiplier) external onlyOwner { | ||
// Check if the multiplier is already whitelisted | ||
for (uint256 i = 0; i < whitelistedMultipliers.length; i++) { | ||
if (whitelistedMultipliers[i] == _multiplier) { | ||
revert MultiplierAlreadyWhitelisted(); | ||
} | ||
} | ||
whitelistedMultipliers.push(_multiplier); | ||
emit MultiplierAdded(_multiplier); | ||
} | ||
|
||
/// @notice Removes a multiplier from the whitelist | ||
/// @param _multiplier The multiplier contract to be removed | ||
function removeMultiplier(IMultiplier _multiplier) external onlyOwner { | ||
bool isWhitelisted = false; | ||
for (uint256 i = 0; i < whitelistedMultipliers.length; i++) { | ||
if (whitelistedMultipliers[i] == _multiplier) { | ||
whitelistedMultipliers[i] = whitelistedMultipliers[whitelistedMultipliers.length - 1]; | ||
whitelistedMultipliers.pop(); | ||
isWhitelisted = true; | ||
emit MultiplierRemoved(_multiplier); | ||
break; | ||
} | ||
} | ||
if (!isWhitelisted) { | ||
revert MultiplierNotWhitelisted(); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,6 +9,7 @@ import {ERC20VotesUpgradeable} from | |
import {Bread} from "bread-token/src/Bread.sol"; | ||
|
||
import {IYieldDistributor} from "src/interfaces/IYieldDistributor.sol"; | ||
import {VotingMultipliers} from "src/VotingMultipliers.sol"; | ||
|
||
/** | ||
* @title Breadchain Yield Distributor | ||
|
@@ -20,7 +21,7 @@ import {IYieldDistributor} from "src/interfaces/IYieldDistributor.sol"; | |
* @custom:coauthor kassandra.eth | ||
* @custom:coauthor theblockchainsocialist.eth | ||
*/ | ||
contract YieldDistributor is IYieldDistributor, OwnableUpgradeable { | ||
contract YieldDistributor is IYieldDistributor, OwnableUpgradeable, VotingMultipliers { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. TBH I'm a little dubious of the upgrade safety here. It would probably be best to implement VotingMultipliers to use ERC7201 storage layouts (like they do with OwnableUpgradeable) to avoid storage layout collisions. It seems complicated but it's actually a pretty trivial one time calculation and some boilerplate code for accessing storage variables. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was thinking we should just deploy a new proxy for this version, and use the setter on the bread contract, as this would also help us resolve #96 |
||
/// @notice The address of the $BREAD token contract | ||
Bread public BREAD; | ||
/// @notice The precision to use for calculations | ||
|
@@ -110,8 +111,13 @@ contract YieldDistributor is IYieldDistributor, OwnableUpgradeable { | |
* @return uint256 The voting power of the user | ||
*/ | ||
function getCurrentVotingPower(address _account) public view returns (uint256) { | ||
return this.getVotingPowerForPeriod(BREAD, previousCycleStartingBlock, lastClaimedBlockNumber, _account) | ||
+ this.getVotingPowerForPeriod(BUTTERED_BREAD, previousCycleStartingBlock, lastClaimedBlockNumber, _account); | ||
uint256 lastCycleStart = lastClaimedBlockNumber - cycleLength; | ||
uint256 multiplier = this.getTotalMultipliers(_account); | ||
multiplier = multiplier == 0 ? 1e18 : multiplier; | ||
RonTuretzky marked this conversation as resolved.
Show resolved
Hide resolved
|
||
uint256 breadVotingPower = this.getVotingPowerForPeriod(BREAD, lastCycleStart, lastClaimedBlockNumber, _account); | ||
uint256 butteredBreadVotingPower = | ||
this.getVotingPowerForPeriod(BUTTERED_BREAD, lastCycleStart, lastClaimedBlockNumber, _account); | ||
return ((breadVotingPower + butteredBreadVotingPower) * multiplier) / PRECISION; | ||
} | ||
|
||
/// @notice Get the current accumulated voting power for a user | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
pragma solidity ^0.8.22; | ||
|
||
import {IMultiplier} from "./multipliers/IMultiplier.sol"; | ||
|
||
/// @title IVotingMultipliers | ||
/// @notice Interface for the VotingMultipliers contract | ||
/// @dev This interface defines the structure and functions for managing voting multipliers | ||
interface IVotingMultipliers { | ||
/// @notice Thrown when attempting to add a multiplier that is already whitelisted | ||
error MultiplierAlreadyWhitelisted(); | ||
/// @notice Thrown when attempting to remove a multiplier that is not whitelisted | ||
error MultiplierNotWhitelisted(); | ||
/// @notice Emitted when a new multiplier is added to the whitelist | ||
/// @param multiplier The address of the added multiplier | ||
|
||
event MultiplierAdded(IMultiplier indexed multiplier); | ||
/// @notice Emitted when a multiplier is removed from the whitelist | ||
/// @param multiplier The address of the removed multiplier | ||
event MultiplierRemoved(IMultiplier indexed multiplier); | ||
/// @notice Returns the multiplier at the specified index in the whitelist | ||
/// @param index The index of the multiplier in the whitelist | ||
/// @return The multiplier contract at the specified index | ||
|
||
function whitelistedMultipliers(uint256 index) external view returns (IMultiplier); | ||
/// @notice Calculates the total multiplier for a given user | ||
/// @param user The address of the user | ||
/// @return The total multiplier value for the user | ||
function getTotalMultipliers(address user) external view returns (uint256); | ||
/// @notice Adds a multiplier to the whitelist | ||
/// @param _multiplier The multiplier contract to be added | ||
function addMultiplier(IMultiplier _multiplier) external; | ||
/// @notice Removes a multiplier from the whitelist | ||
/// @param _multiplier The multiplier contract to be removed | ||
function removeMultiplier(IMultiplier _multiplier) external; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.22; | ||
|
||
import {IProveableMultiplier} from "src/interfaces/multipliers/IProveableMultiplier.sol"; | ||
|
||
/// @title Cross-Chain Proveable Multiplier Interface | ||
/// @notice Interface for contracts that provide a cross-chain proveable multiplying factor | ||
|
||
interface ICrossChainProveableMultiplier is IProveableMultiplier { | ||
/// @notice Get the address of the bridge contract | ||
/// @return The address of the contract used for cross-chain communication | ||
function bridge() external view returns (address); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.22; | ||
|
||
import {INFTMultiplier} from "src/interfaces/multipliers/INFTMultiplier.sol"; | ||
/// @title Dynamic NFT Multiplier Interface | ||
/// @notice Interface for contracts that provide a dynamic multiplying factor for users based on NFT ownership | ||
/// @dev Extends the INFTMultiplier interface with dynamic multiplier functionality | ||
|
||
interface IDynamicNFTMultiplier is INFTMultiplier { | ||
/// @notice Get the multiplying factor for a user | ||
/// @param user The address of the user | ||
/// @return The multiplying factor for the user | ||
function userToFactor(address user) external view returns (uint256); | ||
|
||
/// @notice Get the validity period for a user's factor | ||
/// @param user The address of the user | ||
/// @return The timestamp until which the user's factor is valid | ||
function userToValidity(address user) external view returns (uint256); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.0; | ||
|
||
interface IMultiplier { | ||
/// @notice Returns the voting multiplier for `user`. | ||
function getMultiplyingFactor(address user) external view returns (uint256); | ||
|
||
/// @notice Returns the validity period of the multiplier for `user`. | ||
function validUntil(address user) external view returns (uint256); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.22; | ||
|
||
import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; | ||
import {IMultiplier} from "src/interfaces/multipliers/IMultiplier.sol"; | ||
|
||
/// @title NFT Multiplier Interface | ||
/// @notice Interface for contracts that provide multiplying factors based on NFT ownership | ||
/// @dev Extends the IMultiplier interface with NFT-specific functionality | ||
interface INFTMultiplier is IMultiplier { | ||
/// @notice Get the address of the NFT contract | ||
/// @return The address of the NFT contract used for checking ownership | ||
function NFTAddress() external view returns (IERC721); | ||
|
||
/// @notice Check if a user owns an NFT | ||
/// @param user The address of the user to check | ||
/// @return True if the user owns at least one NFT, false otherwise | ||
function hasNFT(address user) external view returns (bool); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.22; | ||
|
||
import {IProveableMultiplier} from "src/interfaces/multipliers/IProveableMultiplier.sol"; | ||
|
||
/// @title Off-Chain Proveable Multiplier Interface | ||
/// @notice Interface for contracts that provide an off-chain proveable multiplying factor | ||
interface IOffChainProveableMultiplier is IProveableMultiplier { | ||
/// @notice Get the address of the pull oracle | ||
/// @return The address of the oracle used for off-chain data verification | ||
function oracle() external view returns (address); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.22; | ||
|
||
import {IProveableMultiplier} from "src/interfaces/multipliers/IProveableMultiplier.sol"; | ||
/// @title On-Chain Proveable Multiplier Interface | ||
/// @notice Interface for contracts that provide an on-chain proveable multiplying factor | ||
|
||
interface IOnChainProveableMultiplier is IProveableMultiplier { | ||
/// @notice Get the address of the activity contract | ||
/// @return The address of the contract used for verifying on-chain activities | ||
function activityContract() external view returns (address); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.22; | ||
|
||
import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; | ||
import {IDynamicNFTMultiplier} from "src/interfaces/multipliers/IDynamicNFTMultiplier.sol"; | ||
/// @title Proveable Multiplier Interface | ||
/// @notice Interface for contracts that provide a proveable multiplying factor based on user activities | ||
|
||
interface IProveableMultiplier is IERC721, IDynamicNFTMultiplier { | ||
/// @notice Submit activities to potentially earn or upgrade an NFT | ||
/// @param data Encoded data representing the activities | ||
function submitActivities(bytes calldata data) external; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.22; | ||
|
||
import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; | ||
import {INFTMultiplier} from "src/interfaces/multipliers/INFTMultiplier.sol"; | ||
import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; | ||
import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; | ||
|
||
/// @title NFT Multiplier | ||
/// @notice Implementation of INFTMultiplier interface | ||
/// @dev Provides multiplying factors based on NFT ownership | ||
contract NFTMultiplier is INFTMultiplier, Initializable, OwnableUpgradeable { | ||
IERC721 public nftContract; | ||
uint256 public multiplyingFactor; | ||
uint256 public validUntilBlock; | ||
|
||
/// @custom:oz-upgrades-unsafe-allow constructor | ||
constructor() { | ||
_disableInitializers(); | ||
} | ||
|
||
/// @notice Initializer function to set the NFT contract address and initial multiplying factor | ||
/// @param _nftContract Address of the NFT contract | ||
/// @param _initialMultiplyingFactor Initial multiplying factor | ||
/// @param _validUntilBlock Block number until which the multiplier is valid | ||
function initialize(IERC721 _nftContract, uint256 _initialMultiplyingFactor, uint256 _validUntilBlock) | ||
public | ||
initializer | ||
{ | ||
__Ownable_init(msg.sender); | ||
|
||
nftContract = _nftContract; | ||
multiplyingFactor = _initialMultiplyingFactor; | ||
validUntilBlock = _validUntilBlock; | ||
} | ||
|
||
/// @notice Get the address of the NFT contract | ||
/// @return The address of the NFT contract used for checking ownership | ||
function NFTAddress() external view override returns (IERC721) { | ||
return nftContract; | ||
} | ||
|
||
/// @notice Check if a user owns an NFT | ||
/// @param user The address of the user to check | ||
/// @return True if the user owns at least one NFT, false otherwise | ||
function hasNFT(address user) public view override returns (bool) { | ||
return nftContract.balanceOf(user) > 0; | ||
} | ||
|
||
/// @notice Get the multiplying factor for a given user | ||
/// @param user The address of the user | ||
/// @return The multiplying factor if the user owns an NFT, 0 otherwise | ||
function getMultiplyingFactor(address user) external view override returns (uint256) { | ||
return hasNFT(user) ? multiplyingFactor : 0; | ||
} | ||
|
||
/// @notice Get the block number until which the multiplier is valid | ||
/// @return The block number until which the multiplier is valid | ||
function validUntil(address /* user */ ) external view override returns (uint256) { | ||
return validUntilBlock; | ||
} | ||
|
||
/// @notice Update the multiplying factor | ||
/// @param _newMultiplyingFactor New multiplying factor (in basis points) | ||
function updateMultiplyingFactor(uint256 _newMultiplyingFactor) external onlyOwner { | ||
multiplyingFactor = _newMultiplyingFactor; | ||
} | ||
|
||
/// @notice Update the valid until block | ||
/// @param _newValidUntilBlock New block number until which the multiplier is valid | ||
function updateValidUntilBlock(uint256 _newValidUntilBlock) external onlyOwner { | ||
validUntilBlock = _newValidUntilBlock; | ||
} | ||
|
||
/// @notice Update the NFT contract address | ||
/// @param _newNFTContract New NFT contract address | ||
function updateNFTContract(IERC721 _newNFTContract) external onlyOwner { | ||
nftContract = _newNFTContract; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So I think this approach of just a single array is fine, but it's quite in inefficient. Consider most users will probably not have any multipliers. This approach requires every user to check every multiplier that was ever created for every calculation. An alternative approach could be:
An explicit update step to a user's list of multipliers would be required (maybe there's a way to work around that in a way that doesn't tradeoff security? maybe let user's set an "autoapply new multipliers" setting?) and then the ecosystem would only apply those ones. It also gives people more control over their setup. This of course comes with the tradeoff of creating a UI for people to add/remove multipliers, but having a UI for this seems like it would actually be a nice thing to have regardless.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can def change it to mapping > list but I don't think the user should be able to control their multipliers. If it's a non provable multiplier (for example voting streaks) then it should just be activated \ deactivated without their interaction. If it's something they have to prove (like twitter likes for example) then they should have a UI for submitting the proof , but no further than that. I also can't imagine why a user would want to remove a multiplier?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@bagelface why would a UI be needed to add/remove multipliers? Isn't it possible to check in the UI what multipliers from the allowed multipliers the user has and add them automatically?
I also suggest that the original design can be improved by making it possible to query multiple users at onces, thus reducing external calls to multipliers to be proportional to the amount of multipliers, and not a quadratic complexity of m external calls for n users.
Regarding memory concerns (since many users might not have any multipliers at all, returning 1 as a multiplier for most users) it should be noted memory in solidity is not as expensive as it seems:
This means that for 20,000 gas you can expand memory up to ~2500 EVM words, which is 80kb - compared to external calls this cost is negligible
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that this function is only supposed to be called in the context of a single voter voting (that is, it is called within the context of
castVote
).But I'm not sure I understand what you mean, are you referring to making 1 function call to receive the total multiplier for all multipliers? Currently within the design, the multipliers are intended to be separate contracts...Do you mean making it so that multipliers are struct instead? That could def improve efficiency as you say....
However some of these multipliers would require adding custom logic for validation and integrations, which makes it inconvenient as we would need to upgrade the contract for each instance of that happening
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nvm, for some reason I thought this is called for each user in some function to calculate the total voting power, but on a secong look it seems this is called only when a user votes
Edit: yeah saw your edit now
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Check out #71 for more details :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If each multiplier is it's own token contract then we need to either iterate over a master list of token contracts for each voting power calculation or maintain a user => multipliers mapping, which would need to be updated explicitly with each new multiplier token minted. We could potentially build that into the multiplier design. OR we could have some kind of explicit "refresh" function.
Really all I'm suggesting is maybe there is a better way than just maintaining a single master list of all available multiplier token contracts and iterating over them every time a calculation for voting power is made because many users will only have one or two multipliers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The problem is that then we are forcing the user to perform some action in order to add the multiplier to their list. So for example,lets say we have a community call attendance multiplier, which is awarded as an NFT for someone for attending a community call.
In this design, the user would not have to do anything other than attend the call, next time he votes during the master list iteriation the YD will detect they are eligible for a voting multiplier and will apply it.
In the suggested design , the user will have to perform some action (or through account abstraction / refresh function breadchain will have to account for this) in order for the multiplier to be added to their list.
While this is more gas efficient, the tradeoff here is either offchain infrastructure and additional engineering to maintain a synchronized state , or additional friction on the user.
Because we are deploying on Gnosis , the actual additional cost on the user to iterate through the master list is negligible, so my opinion is to ignore it.
The current cost of voting is $0.000169425051222655 and I'm guessing that this iteriation will not increase it in any order of magnitude.
For reference, users can get 0.0005 / week through through the Gnosis Faucet
@rubydusa would love your opinion on this as well
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Im not necessarily suggesting any specific design, just pointing out a potentially issue with the current design. I don't think tx costs are a huge concern, but the block gas limits might (30,000,000). We should run a gas simulation on 1,000 and 10,000 multipliers and see how it looks. If it seems like it might be, maybe we can find some tradeoff that automatically pushes an update to a user's list when a new multiplier is issued.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added tests with 5b9b7b3
It seems the increase is not substantial , and I'm doubting we ever get to 1000 multipliers.