Skip to content
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
27 changes: 15 additions & 12 deletions contracts/upgradeables/soulbounds/Rewards.sol
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,6 @@ contract Rewards is
error InsufficientTreasuryBalance();
error CannotReduceSupply();
error TokenHasReserves();
error SignatureExpired();
error NonceAlreadyUsed();

/*//////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -213,14 +212,13 @@ contract Rewards is

function _decodeData(
bytes calldata _data
) private pure returns (address, uint256, uint256, uint256[] memory) {
) private pure returns (address, uint256, uint256[] memory) {
(
address contractAddress,
uint256 chainId,
uint256 expiration,
uint256[] memory _itemIds
) = abi.decode(_data, (address, uint256, uint256, uint256[]));
return (contractAddress, chainId, expiration, _itemIds);
) = abi.decode(_data, (address, uint256, uint256[]));
return (contractAddress, chainId, _itemIds);
}

function pause() external onlyRole(MANAGER_ROLE) {
Expand Down Expand Up @@ -447,6 +445,7 @@ contract Rewards is
* @return symbols Array of token symbols.
* @return names Array of token names.
* @return types Array of token types ("fa" for fungible assets, "nft" for NFTs).
* @return tokenIds Array of token IDs (0 for ERC20/ERC721, actual rewardTokenId for ERC1155).
*/
function getAllTreasuryBalances()
external
Expand All @@ -458,7 +457,8 @@ contract Rewards is
uint256[] memory availableBalances,
string[] memory symbols,
string[] memory names,
string[] memory types
string[] memory types,
uint256[] memory tokenIds
)
{
return Treasury(treasury).getAllTreasuryBalances(address(this));
Expand Down Expand Up @@ -845,19 +845,13 @@ contract Rewards is
(
address contractAddress,
uint256 chainId,
uint256 expiration,
uint256[] memory tokenIds
) = _decodeData(data);

if (chainId != currentChainId || contractAddress != address(this)) {
revert InvalidInput();
}

// Verify expiration
if (block.timestamp >= expiration) {
revert SignatureExpired();
}

return tokenIds;
}

Expand Down Expand Up @@ -1066,6 +1060,15 @@ contract Rewards is
// Fallback function is called when msg.data is not empty
fallback() external payable {}

function adminVerifySignature(
address to,
uint256 nonce,
bytes calldata data,
bytes calldata signature
) public onlyRole(DEV_CONFIG_ROLE) returns (bool) {
return _verifySignature(to, nonce, data, signature);
}

function addWhitelistSigner(
address _signer
) external onlyRole(DEV_CONFIG_ROLE) {
Expand Down
14 changes: 10 additions & 4 deletions contracts/upgradeables/soulbounds/Treasury.sol
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,8 @@ contract Treasury is Initializable, AccessControlUpgradeable, UUPSUpgradeable, E
uint256[] memory availableBalances,
string[] memory symbols,
string[] memory names,
string[] memory types
string[] memory types,
uint256[] memory tokenIds
)
{
address[] memory whitelistedTokensArray = rewardsState.getWhitelistedTokens();
Expand All @@ -202,6 +203,7 @@ contract Treasury is Initializable, AccessControlUpgradeable, UUPSUpgradeable, E
symbols = new string[](totalCount);
names = new string[](totalCount);
types = new string[](totalCount);
tokenIds = new uint256[](totalCount);

uint256 currentIndex = 0;

Expand All @@ -213,16 +215,18 @@ contract Treasury is Initializable, AccessControlUpgradeable, UUPSUpgradeable, E

if (tokenType == LibItems.RewardType.ERC20) {
_processERC20Token(rewardsContract, tokenAddress, currentIndex, totalBalances, reservedBalances, availableBalances, symbols, names, types);
tokenIds[currentIndex] = 0;
currentIndex++;
} else if (tokenType == LibItems.RewardType.ERC721) {
_processERC721Token(rewardsContract, tokenAddress, currentIndex, totalBalances, reservedBalances, availableBalances, symbols, names, types);
tokenIds[currentIndex] = 0;
currentIndex++;
}
}

currentIndex = _processERC1155Tokens(rewardsContract, erc1155Count, currentIndex, addresses, totalBalances, reservedBalances, availableBalances, symbols, names, types);
currentIndex = _processERC1155Tokens(rewardsContract, erc1155Count, currentIndex, addresses, totalBalances, reservedBalances, availableBalances, symbols, names, types, tokenIds);

return (addresses, totalBalances, reservedBalances, availableBalances, symbols, names, types);
return (addresses, totalBalances, reservedBalances, availableBalances, symbols, names, types, tokenIds);
}

function getTreasuryBalance(address, address _token) external view returns (uint256) {
Expand Down Expand Up @@ -327,7 +331,8 @@ contract Treasury is Initializable, AccessControlUpgradeable, UUPSUpgradeable, E
uint256[] memory availableBalances,
string[] memory symbols,
string[] memory names,
string[] memory types
string[] memory types,
uint256[] memory tokenIds
) private view returns (uint256) {
IRewards rewards = IRewards(rewardsContract);
uint256[] memory itemIds = rewards.getAllItemIds();
Expand Down Expand Up @@ -363,6 +368,7 @@ contract Treasury is Initializable, AccessControlUpgradeable, UUPSUpgradeable, E
processedCount++;

addresses[currentIndex] = erc1155Address;
tokenIds[currentIndex] = erc1155TokenId;

uint256 balance = IERC1155(erc1155Address).balanceOf(address(this), erc1155TokenId);
uint256 reserved = rewardsState.erc1155ReservedAmounts(erc1155Address, erc1155TokenId);
Expand Down
8 changes: 8 additions & 0 deletions test/rewardsSoulbound.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,7 @@ describe('Rewards with Soulbound Tokens', function () {
expect(result.symbols.length).to.equal(0);
expect(result.names.length).to.equal(0);
expect(result.types.length).to.equal(0);
expect(result.tokenIds.length).to.equal(0);
});

it('Should return ERC20 token with type "fa" after deposit to treasury', async function () {
Expand All @@ -477,6 +478,7 @@ describe('Rewards with Soulbound Tokens', function () {
expect(result.symbols[0]).to.equal('MTK');
expect(result.names[0]).to.equal('Mock Token');
expect(result.types[0]).to.equal('fa');
expect(result.tokenIds[0]).to.equal(0n);
});

it('Should return ERC1155 badge with type "nft" after creating reward', async function () {
Expand Down Expand Up @@ -524,13 +526,15 @@ describe('Rewards with Soulbound Tokens', function () {
// First is ERC20 (fa)
expect(result.addresses[0]).to.equal(mockERC20.target);
expect(result.types[0]).to.equal('fa');
expect(result.tokenIds[0]).to.equal(0n);

// Second is ERC1155 (nft)
expect(result.addresses[1]).to.equal(soulboundBadge.target);
expect(result.totalBalances[1]).to.equal(10n); // 10 badges deposited
expect(result.reservedBalances[1]).to.equal(10n); // All reserved for rewards
expect(result.availableBalances[1]).to.equal(0n);
expect(result.types[1]).to.equal('nft');
expect(result.tokenIds[1]).to.equal(BigInt(badgeTokenId));
});

it('Should return both ERC20 and ERC1155 with correct types in mixed reward', async function () {
Expand Down Expand Up @@ -590,12 +594,14 @@ describe('Rewards with Soulbound Tokens', function () {
expect(result.types[erc20Index]).to.equal('fa');
expect(result.symbols[erc20Index]).to.equal('MTK');
expect(result.reservedBalances[erc20Index]).to.equal(ethers.parseEther('50')); // 5 * 10 ETH
expect(result.tokenIds[erc20Index]).to.equal(0n);

// ERC1155 (nft)
const nftIndex = result.addresses.findIndex((addr: string) => addr === soulboundBadge.target);
expect(result.types[nftIndex]).to.equal('nft');
expect(result.totalBalances[nftIndex]).to.equal(10n); // 5 * 2 badges deposited
expect(result.reservedBalances[nftIndex]).to.equal(10n); // All reserved
expect(result.tokenIds[nftIndex]).to.equal(BigInt(badgeTokenId));
});

it('Should update balances after user claims reward', async function () {
Expand Down Expand Up @@ -648,8 +654,10 @@ describe('Rewards with Soulbound Tokens', function () {

expect(result.totalBalances[erc20Index]).to.equal(ethers.parseEther('1000'));
expect(result.reservedBalances[erc20Index]).to.equal(ethers.parseEther('50')); // 10 * 5
expect(result.tokenIds[erc20Index]).to.equal(0n);
expect(result.totalBalances[nftIndex]).to.equal(10n);
expect(result.reservedBalances[nftIndex]).to.equal(10n);
expect(result.tokenIds[nftIndex]).to.equal(BigInt(badgeTokenId));

// Mint reward token to user and claim
await rewards.connect(minterWallet).adminMintById(user1.address, rewardTokenId, 1, true);
Expand Down