Calm Fiery Llama
High
Vouchers pay a donation fee to the subject they vouch for, if the entryDonationFeeBasisPoints
is greater than 0
. Additionally, vouching for mock profiles is allowed in case they are later verified (i.e., registered to an existing profile).
However, since the rewards are tied to the profileId of the subject, if mock profiles are verified, their rewards will not be claimable because their profileId will change to that of the profile registering them.
In EthosVouch.sol
, rewards from donation fees are tied to the subject's profile Id instead of a specific address of that profile.
- The
entryDonationFeeBasisPoints
must be greater than0
. - A mock needs to be active. This occurs, for example, when a review is added to an unregistered address.
None.
- A user vouches for a mock by calling
EthosVouch::vouchByAddress()
. The rewards are tied to the mock's current ProfileId. - Another user calls
EthosProfile::registerAddress()
to register the mock to their profile. The mock's ProfileId is now the one of the profile that registered it. - The original mock calls
EthosVouch::claimRewards()
, but they can only claim the rewards associated with their new ProfileId. This call reverts if the ProfileId does not have any claimable rewards.
A mock cannot claim their rewards if it has been verified with a profile after it has been vouched for. The rewards will be stuck in the contract forever.
The following needs to be added in EthosVouch.test.ts
:
import { type HardhatEthersSigner } from '@nomicfoundation/hardhat-ethers/signers.js';
import { type EthosReview } from '../typechain-types/index.js';
import { common } from './utils/common.js';
const Score = {
Negative: 0,
Neutral: 1,
Positive: 2,
};
type AttestationDetails = {
account: string;
service: string;
};
const defaultComment = 'default comment';
const defaultMetadata = JSON.stringify({ itemKey: 'item value' });
async function allowPaymentToken(
admin: HardhatEthersSigner,
ethosReview: EthosReview,
paymentTokenAddress: string,
isAllowed: boolean,
priceEth: bigint,
): Promise<void> {
await ethosReview.connect(admin).setReviewPrice(isAllowed, paymentTokenAddress, priceEth);
}
Now, the following test can be added in EthosVouch.test.ts
:
it('should fail if rewards for claimed mock', async () => {
const {
ethosProfile,
ethosReview,
ethosVouch,
ADMIN,
OWNER,
PROFILE_CREATOR_0,
PROFILE_CREATOR_1,
OTHER_0,
EXPECTED_SIGNER,
VOUCHER_0,
} = await loadFixture(deployFixture);
await ethosProfile.connect(OWNER).inviteAddress(PROFILE_CREATOR_0.address);
await ethosProfile.connect(PROFILE_CREATOR_0).createProfile(1);
const reviewPrice = ethers.parseEther('1.23456789');
await allowPaymentToken(ADMIN, ethosReview, ethers.ZeroAddress, true, reviewPrice);
const params = {
score: Score.Positive,
subject: OTHER_0.address,
paymentToken: ethers.ZeroAddress,
comment: defaultComment,
metadata: defaultMetadata,
attestationDetails: {
account: '',
service: '',
} satisfies AttestationDetails,
};
await ethosReview
.connect(PROFILE_CREATOR_0)
.addReview(
params.score,
params.subject,
params.paymentToken,
params.comment,
params.metadata,
params.attestationDetails,
{ value: reviewPrice },
);
await ethosProfile.connect(OWNER).inviteAddress(PROFILE_CREATOR_1.address);
await ethosProfile.connect(PROFILE_CREATOR_1).createProfile(1);
await ethosProfile.connect(OWNER).inviteAddress(VOUCHER_0.address);
await ethosProfile.connect(VOUCHER_0).createProfile(1);
expect(await ethosProfile.profileIdByAddress(OTHER_0.address)).to.be.equal(
3,
'wrong mock Id',
);
// set donation fee to 1000
await ethosVouch.connect(ADMIN).setEntryDonationFeeBasisPoints(1000);
expect(await ethosVouch.entryDonationFeeBasisPoints()).to.be.equal(
1000,
'wrong donation fee',
);
await ethosVouch.connect(VOUCHER_0).vouchByProfileId(3, DEFAULT_COMMENT, DEFAULT_METADATA, {
value: ethers.parseEther('1.1'),
});
expect(await ethosVouch.vouchCount()).to.equal(1, 'Wrong vouchCount');
// rewards should be 0.1 eth
expect(await ethosVouch.rewards(3)).to.be.equal(
100000000000000000n,
'wrong rewards',
);
// mock is registered with a profile
const signature = await common.signatureForRegisterAddress(
OTHER_0.address,
'4',
'29548234957',
EXPECTED_SIGNER,
);
await ethosProfile
.connect(PROFILE_CREATOR_1)
.registerAddress(OTHER_0.address, 4, 29548234957, signature);
// profile Id for mock changes
expect(await ethosProfile.profileIdByAddress(OTHER_0.address)).to.be.equal(
4,
'wrong profileId',
);
// new profile Id has its own rewards, in this case 0
expect(await ethosVouch.rewards(4)).to.be.equal(
0,
'wrong rewards',
);
// rewards of old profile Id cannot be claimed, instead it reverts as the new one has 0 rewards
await expect(
ethosVouch
.connect(OTHER_0)
.claimRewards(),
).to.be.revertedWithCustomError(ethosVouch, 'InsufficientRewardsBalance');
});
Consider tying the rewards for a subject that is a mock to its address instead of its ProfileId.