Skip to content

Commit

Permalink
Merge pull request #23 from iotexproject/fix-reward
Browse files Browse the repository at this point in the history
Fix reward pool
  • Loading branch information
ququzone authored Oct 22, 2024
2 parents a3abd4a + f89b916 commit 36c28f2
Show file tree
Hide file tree
Showing 10 changed files with 502 additions and 117 deletions.
1 change: 1 addition & 0 deletions contracts/VestingMaster.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ contract VestingMaster is ReentrancyGuard, Ownable {
uint256 locked;
uint256 timestamp;
}

event Lock(address indexed user, uint256 amount);
event Claim(address indexed user, uint256 amount);
event LockerAdded(address indexed locker);
Expand Down
1 change: 1 addition & 0 deletions contracts/Voter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol
contract Voter is IVoter, ERC2771Context, ReentrancyGuard {
using SafeERC20 for IERC20;
/// @inheritdoc IVoter

address public immutable forwarder;
/// @inheritdoc IVoter
address public strategyManager;
Expand Down
225 changes: 128 additions & 97 deletions contracts/art/BokkyPooBahsDateTimeLibrary.sol

Large diffs are not rendered by default.

3 changes: 1 addition & 2 deletions contracts/art/PerlinNoise.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ pragma solidity 0.8.19;
* @notice An implementation of Perlin Noise that uses 16 bit fixed point arithmetic.
* @notice Updated solidity version from 0.5.0 to 0.8.12.
*/

library PerlinNoise {
/**
* @notice Computes the noise value for a 2D point.
Expand All @@ -22,7 +21,7 @@ library PerlinNoise {
function noise2d(int256 x, int256 y) public pure returns (int256) {
int256 temp = ptable((x >> 16) & 0xff /* Unit square X */);

int256 a = ptable((temp >> 8) + ((y >> 16) & 0xff /* Unit square Y */));
int256 a = ptable((temp >> 8) + ((y >> 16) & 0xff) /* Unit square Y */);
int256 b = ptable((temp & 0xff) + ((y >> 16) & 0xff));

x &= 0xffff; // Square relative X
Expand Down
30 changes: 15 additions & 15 deletions contracts/art/Trig.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ pragma solidity 0.8.19;

library Trig {
/*---
Trigonometry - Implementation 1
---*/
Trigonometry - Implementation 1
---*/

// Implementation 1 requires wrapping inputs between 2 * PI and 4 * PI to avoid precision errors, but it is more accurate than Implementation 2.

Expand Down Expand Up @@ -56,7 +56,7 @@ Trigonometry - Implementation 1
uint8 constant entry_bytes = 4; // each entry in the lookup table is 4 bytes
uint256 constant entry_mask = ((1 << (8 * entry_bytes)) - 1); // mask used to cast bytes32 -> lookup table entry
bytes constant sin_table =
hex"00_00_00_00_00_c9_0f_88_01_92_1d_20_02_5b_26_d7_03_24_2a_bf_03_ed_26_e6_04_b6_19_5d_05_7f_00_35_06_47_d9_7c_07_10_a3_45_07_d9_5b_9e_08_a2_00_9a_09_6a_90_49_0a_33_08_bc_0a_fb_68_05_0b_c3_ac_35_0c_8b_d3_5e_0d_53_db_92_0e_1b_c2_e4_0e_e3_87_66_0f_ab_27_2b_10_72_a0_48_11_39_f0_cf_12_01_16_d5_12_c8_10_6e_13_8e_db_b1_14_55_76_b1_15_1b_df_85_15_e2_14_44_16_a8_13_05_17_6d_d9_de_18_33_66_e8_18_f8_b8_3c_19_bd_cb_f3_1a_82_a0_25_1b_47_32_ef_1c_0b_82_6a_1c_cf_8c_b3_1d_93_4f_e5_1e_56_ca_1e_1f_19_f9_7b_1f_dc_dc_1b_20_9f_70_1c_21_61_b3_9f_22_23_a4_c5_22_e5_41_af_23_a6_88_7e_24_67_77_57_25_28_0c_5d_25_e8_45_b6_26_a8_21_85_27_67_9d_f4_28_26_b9_28_28_e5_71_4a_29_a3_c4_85_2a_61_b1_01_2b_1f_34_eb_2b_dc_4e_6f_2c_98_fb_ba_2d_55_3a_fb_2e_11_0a_62_2e_cc_68_1e_2f_87_52_62_30_41_c7_60_30_fb_c5_4d_31_b5_4a_5d_32_6e_54_c7_33_26_e2_c2_33_de_f2_87_34_96_82_4f_35_4d_90_56_36_04_1a_d9_36_ba_20_13_37_6f_9e_46_38_24_93_b0_38_d8_fe_93_39_8c_dd_32_3a_40_2d_d1_3a_f2_ee_b7_3b_a5_1e_29_3c_56_ba_70_3d_07_c1_d5_3d_b8_32_a5_3e_68_0b_2c_3f_17_49_b7_3f_c5_ec_97_40_73_f2_1d_41_21_58_9a_41_ce_1e_64_42_7a_41_d0_43_25_c1_35_43_d0_9a_ec_44_7a_cd_50_45_24_56_bc_45_cd_35_8f_46_75_68_27_47_1c_ec_e6_47_c3_c2_2e_48_69_e6_64_49_0f_57_ee_49_b4_15_33_4a_58_1c_9d_4a_fb_6c_97_4b_9e_03_8f_4c_3f_df_f3_4c_e1_00_34_4d_81_62_c3_4e_21_06_17_4e_bf_e8_a4_4f_5e_08_e2_4f_fb_65_4c_50_97_fc_5e_51_33_cc_94_51_ce_d4_6e_52_69_12_6e_53_02_85_17_53_9b_2a_ef_54_33_02_7d_54_ca_0a_4a_55_60_40_e2_55_f5_a4_d2_56_8a_34_a9_57_1d_ee_f9_57_b0_d2_55_58_42_dd_54_58_d4_0e_8c_59_64_64_97_59_f3_de_12_5a_82_79_99_5b_10_35_ce_5b_9d_11_53_5c_29_0a_cc_5c_b4_20_df_5d_3e_52_36_5d_c7_9d_7b_5e_50_01_5d_5e_d7_7c_89_5f_5e_0d_b2_5f_e3_b3_8d_60_68_6c_ce_60_ec_38_2f_61_6f_14_6b_61_f1_00_3e_62_71_fa_68_62_f2_01_ac_63_71_14_cc_63_ef_32_8f_64_6c_59_bf_64_e8_89_25_65_63_bf_91_65_dd_fb_d2_66_57_3c_bb_66_cf_81_1f_67_46_c7_d7_67_bd_0f_bc_68_32_57_aa_68_a6_9e_80_69_19_e3_1f_69_8c_24_6b_69_fd_61_4a_6a_6d_98_a3_6a_dc_c9_64_6b_4a_f2_78_6b_b8_12_d0_6c_24_29_5f_6c_8f_35_1b_6c_f9_34_fb_6d_62_27_f9_6d_ca_0d_14_6e_30_e3_49_6e_96_a9_9c_6e_fb_5f_11_6f_5f_02_b1_6f_c1_93_84_70_23_10_99_70_83_78_fe_70_e2_cb_c5_71_41_08_04_71_9e_2c_d1_71_fa_39_48_72_55_2c_84_72_af_05_a6_73_07_c3_cf_73_5f_66_25_73_b5_eb_d0_74_0b_53_fa_74_5f_9d_d0_74_b2_c8_83_75_04_d3_44_75_55_bd_4b_75_a5_85_ce_75_f4_2c_0a_76_41_af_3c_76_8e_0e_a5_76_d9_49_88_77_23_5f_2c_77_6c_4e_da_77_b4_17_df_77_fa_b9_88_78_40_33_28_78_84_84_13_78_c7_ab_a1_79_09_a9_2c_79_4a_7c_11_79_8a_23_b0_79_c8_9f_6d_7a_05_ee_ac_7a_42_10_d8_7a_7d_05_5a_7a_b6_cb_a3_7a_ef_63_23_7b_26_cb_4e_7b_5d_03_9d_7b_92_0b_88_7b_c5_e2_8f_7b_f8_88_2f_7c_29_fb_ed_7c_5a_3d_4f_7c_89_4b_dd_7c_b7_27_23_7c_e3_ce_b1_7d_0f_42_17_7d_39_80_eb_7d_62_8a_c5_7d_8a_5f_3f_7d_b0_fd_f7_7d_d6_66_8e_7d_fa_98_a7_7e_1d_93_e9_7e_3f_57_fe_7e_5f_e4_92_7e_7f_39_56_7e_9d_55_fb_7e_ba_3a_38_7e_d5_e5_c5_7e_f0_58_5f_7f_09_91_c3_7f_21_91_b3_7f_38_57_f5_7f_4d_e4_50_7f_62_36_8e_7f_75_4e_7f_7f_87_2b_f2_7f_97_ce_bc_7f_a7_36_b3_7f_b5_63_b2_7f_c2_55_95_7f_ce_0c_3d_7f_d8_87_8d_7f_e1_c7_6a_7f_e9_cb_bf_7f_f0_94_77_7f_f6_21_81_7f_fa_72_d0_7f_fd_88_59_7f_ff_62_15_7f_ff_ff_ff";
hex"0000000000c90f8801921d20025b26d703242abf03ed26e604b6195d057f00350647d97c0710a34507d95b9e08a2009a096a90490a3308bc0afb68050bc3ac350c8bd35e0d53db920e1bc2e40ee387660fab272b1072a0481139f0cf120116d512c8106e138edbb1145576b1151bdf8515e2144416a81305176dd9de183366e818f8b83c19bdcbf31a82a0251b4732ef1c0b826a1ccf8cb31d934fe51e56ca1e1f19f97b1fdcdc1b209f701c2161b39f2223a4c522e541af23a6887e2467775725280c5d25e845b626a8218527679df42826b92828e5714a29a3c4852a61b1012b1f34eb2bdc4e6f2c98fbba2d553afb2e110a622ecc681e2f8752623041c76030fbc54d31b54a5d326e54c73326e2c233def2873496824f354d905636041ad936ba2013376f9e46382493b038d8fe93398cdd323a402dd13af2eeb73ba51e293c56ba703d07c1d53db832a53e680b2c3f1749b73fc5ec974073f21d4121589a41ce1e64427a41d04325c13543d09aec447acd50452456bc45cd358f46756827471cece647c3c22e4869e664490f57ee49b415334a581c9d4afb6c974b9e038f4c3fdff34ce100344d8162c34e2106174ebfe8a44f5e08e24ffb654c5097fc5e5133cc9451ced46e5269126e53028517539b2aef5433027d54ca0a4a556040e255f5a4d2568a34a9571deef957b0d2555842dd5458d40e8c5964649759f3de125a8279995b1035ce5b9d11535c290acc5cb420df5d3e52365dc79d7b5e50015d5ed77c895f5e0db25fe3b38d60686cce60ec382f616f146b61f1003e6271fa6862f201ac637114cc63ef328f646c59bf64e889256563bf9165ddfbd266573cbb66cf811f6746c7d767bd0fbc683257aa68a69e806919e31f698c246b69fd614a6a6d98a36adcc9646b4af2786bb812d06c24295f6c8f351b6cf934fb6d6227f96dca0d146e30e3496e96a99c6efb5f116f5f02b16fc1938470231099708378fe70e2cbc571410804719e2cd171fa394872552c8472af05a67307c3cf735f662573b5ebd0740b53fa745f9dd074b2c8837504d3447555bd4b75a585ce75f42c0a7641af3c768e0ea576d9498877235f2c776c4eda77b417df77fab988784033287884841378c7aba17909a92c794a7c11798a23b079c89f6d7a05eeac7a4210d87a7d055a7ab6cba37aef63237b26cb4e7b5d039d7b920b887bc5e28f7bf8882f7c29fbed7c5a3d4f7c894bdd7cb727237ce3ceb17d0f42177d3980eb7d628ac57d8a5f3f7db0fdf77dd6668e7dfa98a77e1d93e97e3f57fe7e5fe4927e7f39567e9d55fb7eba3a387ed5e5c57ef0585f7f0991c37f2191b37f3857f57f4de4507f62368e7f754e7f7f872bf27f97cebc7fa736b37fb563b27fc255957fce0c3d7fd8878d7fe1c76a7fe9cbbf7ff094777ff621817ffa72d07ffd88597fff62157fffffff";

/**
* @notice Return the sine of a value, specified in radians scaled by 1e18
Expand Down Expand Up @@ -142,15 +142,15 @@ Trigonometry - Implementation 1
}

/*---
Trigonometry - Implementation 2
---*/
Trigonometry - Implementation 2
---*/

// Implementation 2 is less accurate than Implementation 1, but it allows for unbounded input in degrees. Output should be downscaled by 1e6. Sintable array loaded twice to avoid stack too deep errors.

//Computes sine using degrees unit input.
function dsin(int degrees) external pure returns (int) {
int[360] memory sintable = [
int(0),
function dsin(int256 degrees) external pure returns (int256) {
int256[360] memory sintable = [
int256(0),
17452,
34899,
52336,
Expand Down Expand Up @@ -512,16 +512,16 @@ Trigonometry - Implementation 2
-17452
];
if (degrees > -1) {
return sintable[uint(degrees) % 360];
return sintable[uint256(degrees) % 360];
} else {
return sintable[uint(degrees * -1) % 360] * -1;
return sintable[uint256(degrees * -1) % 360] * -1;
}
}

//Computes cosine with degrees unit input.
function dcos(int degrees) external pure returns (int) {
int[360] memory sintable = [
int(0),
function dcos(int256 degrees) external pure returns (int256) {
int256[360] memory sintable = [
int256(0),
17452,
34899,
52336,
Expand Down Expand Up @@ -883,9 +883,9 @@ Trigonometry - Implementation 2
-17452
];
if ((degrees + 90) > -1) {
return sintable[uint(degrees + 90) % 360];
return sintable[uint256(degrees + 90) % 360];
} else {
return sintable[uint((degrees + 90) * -1) % 360] * -1;
return sintable[uint256((degrees + 90) * -1) % 360] * -1;
}
}
}
8 changes: 6 additions & 2 deletions contracts/factories/FactoryRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ contract FactoryRegistry is IFactoryRegistry, Ownable {
address gaugeFactory;
}
/// @dev the factories linked to the poolFactory

mapping(address => FactoriesToPoolFactory) private _factoriesToPoolsFactory;

constructor(address _fallbackPoolFactory, address _fallbackIncentiveFactory, address _fallbackGaugeFactory) {
Expand All @@ -36,7 +37,9 @@ contract FactoryRegistry is IFactoryRegistry, Ownable {

/// @inheritdoc IFactoryRegistry
function approve(address poolFactory, address incentiveFactory, address gaugeFactory) public onlyOwner {
if (poolFactory == address(0) || incentiveFactory == address(0) || gaugeFactory == address(0)) revert ZeroAddress();
if (poolFactory == address(0) || incentiveFactory == address(0) || gaugeFactory == address(0)) {
revert ZeroAddress();
}
if (_poolFactories.contains(poolFactory)) revert PathAlreadyApproved();

FactoriesToPoolFactory memory usedFactories = _factoriesToPoolsFactory[poolFactory];
Expand All @@ -48,8 +51,9 @@ contract FactoryRegistry is IFactoryRegistry, Ownable {
} else {
// If the poolFactory *has* been approved before, can only approve the same used gauge/votingRewards factory to
// to maintain state within Voter
if (incentiveFactory != usedFactories.incentiveFactory || gaugeFactory != usedFactories.gaugeFactory)
if (incentiveFactory != usedFactories.incentiveFactory || gaugeFactory != usedFactories.gaugeFactory) {
revert InvalidFactoriesToPoolFactory();
}
}

_poolFactories.add(poolFactory);
Expand Down
192 changes: 192 additions & 0 deletions contracts/gauges/FixedRewardPool.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import {ERC721Holder} from "@openzeppelin/contracts/token/ERC721/utils/ERC721Holder.sol";
import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import {ReentrancyGuardUpgradeable} from "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";

import {IWeightedNFT} from "../interfaces/IWeightedNFT.sol";

contract FixedRewardPool is OwnableUpgradeable, ReentrancyGuardUpgradeable, ERC721Holder {
event Deposit(address indexed user, uint256 tokenId, uint256 rewards, uint256 weight);
event Withdraw(address indexed user, uint256 tokenId, uint256 rewards, uint256 weight);
event EmergencyWithdraw(address indexed user, uint256 tokenId, uint256 weight);
event Poke(address indexed user, uint256 tokenId, uint256 rewards, uint256 originalWeight, uint256 newWeight);
event ClaimRewards(address indexed user, uint256 rewards);

struct UserInfo {
uint256 amount; // How many staked tokens the user has provided
uint256 rewardDebt; // Reward debt
}

// The precision factor
uint256 immutable PRECISION_FACTOR = 1e12;

// The weighted NFT contract
IWeightedNFT public weightNFT;
// Accrued token per share
uint256 public accTokenPerShare;
// The block number of the last pool update
uint256 public lastRewardBlock;
// Reward tokens created per block.
uint256 public rewardPerBlock;
// Total staked weight
uint256 public totalStakedWeight;
// Info of each user that stakes tokens (stakedToken)
mapping(address => UserInfo) public userInfo;

// Mapping from tokenId to staker
mapping(uint256 => address) public tokenStaker;
// Mapping from tokenId to weight
mapping(uint256 => uint256) public tokenWeight;

function initialize(address _nft, uint256 _startBlock, uint256 _rewardPerBlock) external initializer {
require(_startBlock >= block.number, "invalid start block");
require(_rewardPerBlock > 0, "invalid reward per block");

__Ownable_init();
__ReentrancyGuard_init();
weightNFT = IWeightedNFT(_nft);
lastRewardBlock = _startBlock;
rewardPerBlock = _rewardPerBlock;
}

function deposit(uint256 _tokenId) external nonReentrant {
UserInfo storage user = userInfo[msg.sender];
_updatePool();

uint256 _pending = 0;
if (user.amount > 0) {
_pending = (user.amount * accTokenPerShare) / PRECISION_FACTOR - user.rewardDebt;
if (_pending > 0) {
(bool success, ) = msg.sender.call{value: _pending}("");
require(success, "Failed to send reward");
}
}

uint _amount = weightNFT.weight(_tokenId);
if (_amount > 0) {
user.amount = user.amount + _amount;
IERC721(weightNFT.nft()).safeTransferFrom(msg.sender, address(this), _tokenId);
tokenStaker[_tokenId] = msg.sender;
tokenWeight[_tokenId] = _amount;
totalStakedWeight = totalStakedWeight + _amount;
}

user.rewardDebt = (user.amount * accTokenPerShare) / PRECISION_FACTOR;

emit Deposit(msg.sender, _tokenId, _pending, _amount);
}

function withdraw(uint256 _tokenId) external nonReentrant {
require(tokenStaker[_tokenId] == msg.sender, "Invalid staker");
UserInfo storage user = userInfo[msg.sender];

_updatePool();

uint256 _pending = (user.amount * accTokenPerShare) / PRECISION_FACTOR - user.rewardDebt;

uint256 _amount = tokenWeight[_tokenId];
user.amount = user.amount - _amount;
IERC721(weightNFT.nft()).safeTransferFrom(address(this), msg.sender, _tokenId);
tokenStaker[_tokenId] = address(0);
tokenWeight[_tokenId] = 0;
totalStakedWeight = totalStakedWeight - _amount;

if (_pending > 0) {
(bool success, ) = msg.sender.call{value: _pending}("");
require(success, "Failed to send reward");
}

user.rewardDebt = (user.amount * accTokenPerShare) / PRECISION_FACTOR;

emit Withdraw(msg.sender, _tokenId, _pending, _amount);
}

function emergencyWithdraw(uint256 _tokenId) external nonReentrant {
require(tokenStaker[_tokenId] == msg.sender, "Invalid staker");
UserInfo storage user = userInfo[msg.sender];

uint256 _amount = tokenWeight[_tokenId];

user.amount = user.amount - _amount;
IERC721(weightNFT.nft()).safeTransferFrom(address(this), msg.sender, _tokenId);
tokenStaker[_tokenId] = address(0);
tokenWeight[_tokenId] = 0;
totalStakedWeight = totalStakedWeight - _amount;

emit EmergencyWithdraw(msg.sender, _tokenId, user.amount);
}

function poke(uint256 _tokenId) external nonReentrant {
address _staker = tokenStaker[_tokenId];
require(_staker != address(0), "Invalid token");

UserInfo storage user = userInfo[_staker];

_updatePool();

uint256 _pending = (user.amount * accTokenPerShare) / PRECISION_FACTOR - user.rewardDebt;
if (_pending > 0) {
(bool success, ) = _staker.call{value: _pending}("");
require(success, "Failed to send reward");
}

uint256 _originalWeight = tokenWeight[_tokenId];
uint256 _newWeight = weightNFT.weight(_tokenId);
if (_originalWeight != _newWeight) {
user.amount = user.amount - _originalWeight + _newWeight;
tokenWeight[_tokenId] = _newWeight;
totalStakedWeight = totalStakedWeight - _originalWeight + _newWeight;
}

user.rewardDebt = (user.amount * accTokenPerShare) / PRECISION_FACTOR;

emit Poke(_staker, _tokenId, _pending, _originalWeight, _newWeight);
}

function claimRewards(address _user) external nonReentrant {
UserInfo storage user = userInfo[_user];

_updatePool();

uint256 _pending = (user.amount * accTokenPerShare) / PRECISION_FACTOR - user.rewardDebt;
if (_pending > 0) {
(bool success, ) = _user.call{value: _pending}("");
require(success, "Failed to send reward");
}

user.rewardDebt = (user.amount * accTokenPerShare) / PRECISION_FACTOR;
emit ClaimRewards(_user, _pending);
}

function pendingReward(address _user) external view returns (uint256) {
UserInfo storage user = userInfo[_user];
uint256 _totalStakedWeight = totalStakedWeight;
if (block.number > lastRewardBlock && _totalStakedWeight != 0) {
uint256 rewards = (block.number - lastRewardBlock) * rewardPerBlock;
uint256 adjustedTokenPerShare = accTokenPerShare + (rewards * PRECISION_FACTOR) / _totalStakedWeight;
return (user.amount * adjustedTokenPerShare) / PRECISION_FACTOR - user.rewardDebt;
} else {
return (user.amount * accTokenPerShare) / PRECISION_FACTOR - user.rewardDebt;
}
}

function _updatePool() internal {
if (block.number <= lastRewardBlock) {
return;
}

if (totalStakedWeight == 0) {
lastRewardBlock = block.number;
return;
}

uint256 rewards = (block.number - lastRewardBlock) * rewardPerBlock;
accTokenPerShare = accTokenPerShare + (rewards * PRECISION_FACTOR) / totalStakedWeight;
lastRewardBlock = block.number;
}

receive() external payable {}
}
3 changes: 2 additions & 1 deletion contracts/test/TestDeviceNFT.sol
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {ERC721} from "@openzeppelin/contracts/token/ERC721/ERC721.sol";
Expand Down Expand Up @@ -27,7 +28,7 @@ contract TestDeviceNFT is IWeightedNFT, ERC721 {
weightOf[_tokenId] = _weight;
}

function mint(address to, uint tokenId) external {
function mint(address to, uint256 tokenId) external {
_mint(to, tokenId);
weightOf[tokenId] = 1 ether;
}
Expand Down
38 changes: 38 additions & 0 deletions contracts/wrapper/OwnedWeightedNFT.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";

import {IWeightedNFT} from "../interfaces/IWeightedNFT.sol";

contract OwnedWeightedNFT is Ownable, IWeightedNFT {
event WeightChanged(uint256 tokenId, uint256 weight);

uint256 public immutable DEFAULT_WEIGHT = 100;

address public immutable override nft;
mapping(uint256 => uint256) _weights;

constructor(address _nft, address _owner) {
nft = _nft;
transferOwnership(_owner);
}

function weight(uint256 tokenId) external view override returns (uint256) {
require(IERC721(nft).ownerOf(tokenId) != address(0), "token not minted");

uint256 _weight = _weights[tokenId];
if (_weight == 0) {
return DEFAULT_WEIGHT;
}
return _weights[tokenId];
}

function setWeight(uint256 tokenId, uint256 _weight) external onlyOwner {
require(_weight > 0, "invalid weight");

_weights[tokenId] = _weight;
emit WeightChanged(tokenId, _weight);
}
}
Loading

0 comments on commit 36c28f2

Please sign in to comment.