Skip to content

Commit

Permalink
change to signing the bytes array
Browse files Browse the repository at this point in the history
  • Loading branch information
waynehoover committed Oct 25, 2023
1 parent baf67d3 commit 7fedc42
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 26 deletions.
56 changes: 33 additions & 23 deletions contracts/QuestFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {SafeTransferLib} from "solady/utils/SafeTransferLib.sol";
import {IERC1155} from "openzeppelin-contracts/token/ERC1155/IERC1155.sol";
import {IQuestOwnable} from "./interfaces/IQuestOwnable.sol";
import {IQuest1155Ownable} from "./interfaces/IQuest1155Ownable.sol";
import "forge-std/console.sol";

/// @title QuestFactory
/// @author RabbitHole.gg
Expand Down Expand Up @@ -105,19 +106,20 @@ contract QuestFactory is Initializable, LegacyStorage, OwnableRoles, IQuestFacto

modifier claimChecks(ClaimData memory claimData_) {
Quest storage currentQuest = quests[claimData_.questId];
bytes memory hashc = abi.encodePacked(msg.sender, claimData_.questId);
bytes memory hashc = abi.encodePacked(claimData_.claimer, claimData_.questId);
bytes32 encodedHash;

if (claimData_.ref != address(0)) {
hashc = abi.encodePacked(hashc, claimData_.ref);
}
if (bytes(claimData_.extraData).length > 0){
hashc = abi.encodePacked(hashc, claimData_.extraData);
encodedHash = claimData_.hashBytes;
} else {
encodedHash = keccak256(hashc);
}

bytes32 encodedHash = keccak256(hashc);

if (currentQuest.numberMinted + 1 > currentQuest.totalParticipants) revert OverMaxAllowedToMint();
if (currentQuest.addressMinted[msg.sender]) revert AddressAlreadyMinted();
if (currentQuest.addressMinted[claimData_.claimer]) revert AddressAlreadyMinted();
if (encodedHash != claimData_.hashBytes) revert InvalidHash();
if (recoverSigner(claimData_.hashBytes, claimData_.signature) != claimSignerAddress) revert AddressNotSigned();
_;
Expand Down Expand Up @@ -308,13 +310,21 @@ contract QuestFactory is Initializable, LegacyStorage, OwnableRoles, IQuestFacto
/// @param signature_ The signature of the data
/// @param data_ The data to decode for the claim
function claim(bytes calldata signature_, bytes calldata data_) external payable {
(address ref_, string memory questId_, string memory jsonData_) = abi.decode(data_, (address, string, string));
bytes32 hash_ = keccak256(abi.encodePacked(msg.sender, questId_, ref_, jsonData_));
(
address claimer_,
address ref_,
string memory questId_,
string memory jsonData_
) = abi.decode(
data_,
(address, address, string, string)
);
bytes32 hash_ = keccak256(data_);

if (quests[questId_].questType.eq("erc1155")) {
claim1155RewardsRef(ClaimData(questId_, hash_, signature_, ref_, 0, jsonData_));
claim1155RewardsRef(ClaimData(questId_, hash_, signature_, ref_, claimer_, jsonData_));
} else { // erc20, erc20Stream
claimRewardsRef(ClaimData(questId_, hash_, signature_, ref_, 0, jsonData_));
claimRewardsRef(ClaimData(questId_, hash_, signature_, ref_, claimer_, jsonData_));
}
}

Expand All @@ -325,9 +335,9 @@ contract QuestFactory is Initializable, LegacyStorage, OwnableRoles, IQuestFacto
/// @param ref_ The referral address
function claim(string memory questId_, bytes32 hash_, bytes memory signature_, address ref_) external payable {
if (quests[questId_].questType.eq("erc1155")) {
claim1155RewardsRef(ClaimData(questId_, hash_, signature_, ref_, 0, ""));
claim1155RewardsRef(ClaimData(questId_, hash_, signature_, ref_, msg.sender, ""));
} else { // erc20, erc20Stream
claimRewardsRef(ClaimData(questId_, hash_, signature_, ref_, 0, ""));
claimRewardsRef(ClaimData(questId_, hash_, signature_, ref_, msg.sender, ""));
}
}

Expand All @@ -336,15 +346,15 @@ contract QuestFactory is Initializable, LegacyStorage, OwnableRoles, IQuestFacto
/// @param hash_ The hash of the message
/// @param signature_ The signature of the hash
function claim1155Rewards(string memory questId_, bytes32 hash_, bytes memory signature_) external payable {
claim1155RewardsRef(ClaimData(questId_, hash_, signature_, address(0), 0, ""));
claim1155RewardsRef(ClaimData(questId_, hash_, signature_, address(0), msg.sender, ""));
}

/// @dev claim rewards for a quest
/// @param questId_ The id of the quest
/// @param hash_ The hash of the message
/// @param signature_ The signature of the hash
function claimRewards(string memory questId_, bytes32 hash_, bytes memory signature_) external payable {
claimRewardsRef(ClaimData(questId_, hash_, signature_, address(0), 0, ""));
claimRewardsRef(ClaimData(questId_, hash_, signature_, address(0), msg.sender, ""));
}

/*//////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -551,25 +561,25 @@ contract QuestFactory is Initializable, LegacyStorage, OwnableRoles, IQuestFacto
if (block.timestamp < questContract_.startTime()) revert QuestNotStarted();
if (block.timestamp > questContract_.endTime()) revert QuestEnded();

currentQuest.addressMinted[msg.sender] = true;
currentQuest.addressMinted[claimData_.claimer] = true;
++currentQuest.numberMinted;
questContract_.singleClaim(msg.sender);
questContract_.singleClaim(claimData_.claimer);

if (mintFee > 0) processMintFee(claimData_.ref, currentQuest.questCreator, claimData_.questId);

emit QuestClaimedData(
msg.sender,
claimData_.claimer,
currentQuest.questAddress,
claimData_.extraData
);

emit Quest1155Claimed(
msg.sender, currentQuest.questAddress, claimData_.questId, questContract_.rewardToken(), questContract_.tokenId()
claimData_.claimer, currentQuest.questAddress, claimData_.questId, questContract_.rewardToken(), questContract_.tokenId()
);

if (claimData_.ref != address(0)) {
emit QuestClaimedReferred(
msg.sender,
claimData_.claimer,
currentQuest.questAddress,
claimData_.questId,
questContract_.rewardToken(),
Expand All @@ -594,20 +604,20 @@ contract QuestFactory is Initializable, LegacyStorage, OwnableRoles, IQuestFacto
if (block.timestamp < questContract_.startTime()) revert QuestNotStarted();
if (block.timestamp > questContract_.endTime()) revert QuestEnded();

currentQuest.addressMinted[msg.sender] = true;
currentQuest.addressMinted[claimData_.claimer] = true;
++currentQuest.numberMinted;
questContract_.singleClaim(msg.sender);
questContract_.singleClaim(claimData_.claimer);

if (mintFee > 0) processMintFee(claimData_.ref, currentQuest.questCreator, claimData_.questId);

emit QuestClaimedData(
msg.sender,
claimData_.claimer,
currentQuest.questAddress,
claimData_.extraData
);

emit QuestClaimed(
msg.sender,
claimData_.claimer,
currentQuest.questAddress,
claimData_.questId,
questContract_.rewardToken(),
Expand All @@ -616,7 +626,7 @@ contract QuestFactory is Initializable, LegacyStorage, OwnableRoles, IQuestFacto

if (claimData_.ref != address(0)) {
emit QuestClaimedReferred(
msg.sender,
claimData_.claimer,
currentQuest.questAddress,
claimData_.questId,
questContract_.rewardToken(),
Expand Down
2 changes: 1 addition & 1 deletion contracts/interfaces/IQuestFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ interface IQuestFactory {
bytes32 hashBytes;
bytes signature;
address ref;
uint256 amount;
address claimer;
string extraData;
}

Expand Down
50 changes: 48 additions & 2 deletions test/QuestFactory.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -370,9 +370,9 @@ contract TestQuestFactory is Test, Errors, Events, TestUtils {

string memory json = string(abi.encodePacked('{"anyting": "we want", "foo": "bar"}'));

bytes32 msgHash = keccak256(abi.encodePacked(participant, "questId2", referrer, json));
bytes memory data = abi.encode(participant, referrer, "questId2", json);
bytes32 msgHash = keccak256(data);
bytes memory signature = signHash(msgHash, claimSignerPrivateKey);
bytes memory data = abi.encode(referrer, "questId2", json);

vm.startPrank(participant);
vm.recordLogs();
Expand All @@ -395,6 +395,52 @@ contract TestQuestFactory is Test, Errors, Events, TestUtils {
assertEq(abi.decode(entries[3].data, (string)), json);
}

function test_claim_with_bytes_no_referrer() public{
vm.startPrank(owner);
questFactory.setRewardAllowlistAddress(address(sampleERC20), true);

vm.startPrank(questCreator);
sampleERC20.approve(address(questFactory), calculateTotalRewardsPlusFee(TOTAL_PARTICIPANTS, REWARD_AMOUNT, QUEST_FEE));
address questAddress = questFactory.createQuestAndQueue(
address(sampleERC20),
END_TIME,
START_TIME,
TOTAL_PARTICIPANTS,
REWARD_AMOUNT,
"questId2",
"actionSpec",
0
);

uint256 questCreatorBeforeBalance = questCreator.balance;
vm.warp(START_TIME + 1);

string memory json = string(abi.encodePacked('{"anyting": "we want", "foo": "bar"}'));

bytes memory data = abi.encode(participant, address(0), "questId2", json);
bytes32 msgHash = keccak256(data);
bytes memory signature = signHash(msgHash, claimSignerPrivateKey);

vm.startPrank(participant);
vm.recordLogs();
questFactory.claim{value: MINT_FEE}(signature, data);

// erc20 reward
assertEq(sampleERC20.balanceOf(participant), REWARD_AMOUNT, "particpiant erc20 balance");

// claim fee rewards
assertEq(questCreator.balance - questCreatorBeforeBalance, MINT_FEE / 3, "questCreator mint fee");
assertEq(protocolFeeRecipient.balance, (MINT_FEE / 3) * 2, "protocolFeeRecipient mint fee");

// assert QuestClaimedData event
Vm.Log[] memory entries = vm.getRecordedLogs();
assertEq(entries.length, 5);
assertEq(entries[3].topics[0], keccak256("QuestClaimedData(address,address,string)"));
assertEq(entries[3].topics[1], bytes32(uint256(uint160(participant))));
assertEq(entries[3].topics[2], bytes32(uint256(uint160(questAddress))));
assertEq(abi.decode(entries[3].data, (string)), json);
}

function test_claim_with_claimRewards_without_referrer() public{
vm.startPrank(owner);
questFactory.setRewardAllowlistAddress(address(sampleERC20), true);
Expand Down

0 comments on commit 7fedc42

Please sign in to comment.