Skip to content

Commit

Permalink
[ADHOC] Fix decodeClaimData for ERC20PeggedVariableCriteriaIncentive (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
jonathandiep authored Dec 20, 2024
1 parent 3012fe3 commit f01db75
Show file tree
Hide file tree
Showing 3 changed files with 161 additions and 32 deletions.
5 changes: 5 additions & 0 deletions .changeset/wicked-humans-fix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@boostxyz/sdk": patch
---

fixes claim data decoding for ERC20PeggedVariableCriteriaIncentive
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import {
gasRebateIncentiveCriteria,
} from "./ERC20VariableCriteriaIncentive";
import { allKnownSignatures } from "@boostxyz/test/allKnownSignatures";
import { readMockErc20BalanceOf } from "@boostxyz/evm";

/**
* A basic ERC721 mint scalar criteria for testing
Expand Down Expand Up @@ -73,11 +74,11 @@ export function basicErc721MintScalarCriteria(
let fixtures: Fixtures,
erc20: MockERC20,
erc721: MockERC721,
erc20Incentive: ERC20PeggedVariableCriteriaIncentive,
erc20PeggedVariableCriteriaIncentive: ERC20PeggedVariableCriteriaIncentive,
budgets: BudgetFixtures,
boost: Boost;

describe("ERC20VariableCriteriaIncentive", () => {
describe("ERC20PeggedVariableCriteriaIncentive", () => {
beforeAll(async () => {
fixtures = await loadFixture(deployFixtures(defaultOptions));
});
Expand All @@ -86,7 +87,7 @@ describe("ERC20VariableCriteriaIncentive", () => {
budgets = await loadFixture(fundBudget(defaultOptions, fixtures));
erc20 = await loadFixture(fundErc20(defaultOptions));
erc721 = await loadFixture(fundErc721(defaultOptions));
erc20Incentive = fixtures.core.ERC20PeggedVariableCriteriaIncentive({
erc20PeggedVariableCriteriaIncentive = fixtures.core.ERC20PeggedVariableCriteriaIncentive({
asset: budgets.erc20.assertValidAddress(),
reward: parseEther("1"),
limit: parseEther("10"),
Expand All @@ -97,48 +98,48 @@ describe("ERC20VariableCriteriaIncentive", () => {

boost = await freshBoost(fixtures, {
budget: budgets.budget,
incentives: [erc20Incentive],
incentives: [erc20PeggedVariableCriteriaIncentive],
});
expect(isAddress(boost.incentives[0]!.assertValidAddress())).toBe(true);
});

describe("Basic Parameters", () => {
test("should return correct asset address", async () => {
const asset = await erc20Incentive.asset();
const asset = await erc20PeggedVariableCriteriaIncentive.asset();
expect(isAddressEqual(asset, budgets.erc20.assertValidAddress())).toBe(true);
});

test("should return correct peg token address", async () => {
const peg = await erc20Incentive.peg();
const peg = await erc20PeggedVariableCriteriaIncentive.peg();
expect(isAddressEqual(peg, erc20.assertValidAddress())).toBe(true);
});

test("should return correct reward amount", async () => {
const reward = await erc20Incentive.reward();
const reward = await erc20PeggedVariableCriteriaIncentive.reward();
expect(reward).toBe(parseEther("1"));
});

test("should return correct limit", async () => {
const limit = await erc20Incentive.limit();
const limit = await erc20PeggedVariableCriteriaIncentive.limit();
expect(limit).toBe(parseEther("10"));
});

test("should return correct max reward", async () => {
const maxReward = await erc20Incentive.getMaxReward();
const maxReward = await erc20PeggedVariableCriteriaIncentive.getMaxReward();
expect(maxReward).toBe(parseEther("20"));
});
});

describe("Remaining Claims", () => {
test("should calculate remaining claim potential correctly", async () => {
const remaining = await erc20Incentive.getRemainingClaimPotential();
const limit = await erc20Incentive.limit();
const totalClaimed = await erc20Incentive.totalClaimed();
const remaining = await erc20PeggedVariableCriteriaIncentive.getRemainingClaimPotential();
const limit = await erc20PeggedVariableCriteriaIncentive.limit();
const totalClaimed = await erc20PeggedVariableCriteriaIncentive.totalClaimed();
expect(remaining).toBe(limit - totalClaimed);
});

test("should return true for canBeClaimed when claims remain", async () => {
const canBeClaimed = await erc20Incentive.canBeClaimed();
const canBeClaimed = await erc20PeggedVariableCriteriaIncentive.canBeClaimed();
expect(canBeClaimed).toBe(true);
});
});
Expand All @@ -159,7 +160,7 @@ describe("ERC20VariableCriteriaIncentive", () => {
describe("Claim Data", () => {
test("should properly encode claim data", () => {
const rewardAmount = parseEther("1");
const encodedData = erc20Incentive.buildClaimData(rewardAmount);
const encodedData = erc20PeggedVariableCriteriaIncentive.buildClaimData(rewardAmount);
expect(encodedData).toBe(
"0x0000000000000000000000000000000000000000000000000de0b6b3a7640000"
);
Expand All @@ -168,14 +169,14 @@ describe("ERC20VariableCriteriaIncentive", () => {

describe("Owner", () => {
test("should return correct owner", async () => {
const owner = await erc20Incentive.owner();
const owner = await erc20PeggedVariableCriteriaIncentive.owner();
expect(isAddressEqual(owner, fixtures.core.assertValidAddress())).toBe(true);
});
});

describe("Current Reward", () => {
test("should return valid current reward", async () => {
const currentReward = await erc20Incentive.currentReward();
const currentReward = await erc20PeggedVariableCriteriaIncentive.currentReward();
expect(currentReward).toBeDefined();
expect(currentReward).toBeTypeOf("bigint");
});
Expand All @@ -190,7 +191,7 @@ describe("ERC20VariableCriteriaIncentive", () => {
recipient,
1n,
);
const scalar = await erc20Incentive.getIncentiveScalar({
const scalar = await erc20PeggedVariableCriteriaIncentive.getIncentiveScalar({
hash,
chainId: 31337,
knownSignatures: allKnownSignatures,
Expand All @@ -202,15 +203,15 @@ describe("ERC20VariableCriteriaIncentive", () => {
test("should return a valid scalar for event-based criteria", async () => {
boost = await freshBoost(fixtures, {
budget: budgets.budget,
incentives: [erc20Incentive],
incentives: [erc20PeggedVariableCriteriaIncentive],
});
const recipient = accounts[1].account;
const { hash } = await erc721.transferFromRaw(
accounts[0].account,
recipient,
1n,
);
const scalar = await erc20Incentive.getIncentiveScalar({
const scalar = await erc20PeggedVariableCriteriaIncentive.getIncentiveScalar({
hash,
chainId: 31337,
knownSignatures: allKnownSignatures,
Expand All @@ -223,7 +224,7 @@ describe("ERC20VariableCriteriaIncentive", () => {
// Ensure that the gasRebateIncentiveCriteria returns the correct structure
const gasRebateCriteria = gasRebateIncentiveCriteria();

erc20Incentive = fixtures.core.ERC20PeggedVariableCriteriaIncentive({
erc20PeggedVariableCriteriaIncentive = fixtures.core.ERC20PeggedVariableCriteriaIncentive({
asset: budgets.erc20.assertValidAddress(),
reward: 1n,
limit: 1n,
Expand All @@ -242,7 +243,7 @@ describe("ERC20VariableCriteriaIncentive", () => {

boost = await freshBoost(fixtures, {
budget: budgets.budget,
incentives: [erc20Incentive],
incentives: [erc20PeggedVariableCriteriaIncentive],
});

// Validate that the deployed incentive has the correct criteria set up
Expand All @@ -261,7 +262,7 @@ describe("ERC20VariableCriteriaIncentive", () => {
const { hash } = await erc20.mintRaw(recipient, parseEther("100"));

try {
await erc20Incentive.getIncentiveScalar({
await erc20PeggedVariableCriteriaIncentive.getIncentiveScalar({
hash,
chainId: 31337,
knownSignatures: allKnownSignatures,
Expand All @@ -276,7 +277,7 @@ describe("ERC20VariableCriteriaIncentive", () => {
const { hash } = await erc20.mintRaw(recipient, parseEther("100"));

try {
await erc20Incentive.getIncentiveScalar({
await erc20PeggedVariableCriteriaIncentive.getIncentiveScalar({
hash,
chainId: 31337,
knownSignatures: allKnownSignatures,
Expand All @@ -299,11 +300,11 @@ describe("ERC20VariableCriteriaIncentive", () => {
// rebase this
const boost = await freshBoost(fixtures, {
budget: budgets.budget,
incentives: [erc20Incentive],
incentives: [erc20PeggedVariableCriteriaIncentive],
});
const [amount, address] = await budgets.budget.clawbackFromTarget(
fixtures.core.assertValidAddress(),
erc20Incentive.buildClawbackData(1n),
erc20PeggedVariableCriteriaIncentive.buildClawbackData(1n),
boost.id,
0,
);
Expand All @@ -312,4 +313,45 @@ describe("ERC20VariableCriteriaIncentive", () => {
true,
);
});

test('can claim', async () => {
const referrer = accounts.at(1)?.account!
const trustedSigner = accounts.at(0)!
const claimant = trustedSigner.account!;
const asset = await erc20PeggedVariableCriteriaIncentive.asset()
const signedAmount = 2n
const incentiveData = erc20PeggedVariableCriteriaIncentive.buildClaimData(signedAmount)
const claimDataPayload = await boost.validator.encodeClaimData({
signer: trustedSigner,
incentiveData,
chainId: defaultOptions.config.chains[0].id,
incentiveQuantity: boost.incentives.length,
claimant,
boostId: boost.id
})

// verify reward amount
const incentiveClaimAmount = await erc20PeggedVariableCriteriaIncentive.decodeClaimData(claimDataPayload)
expect(incentiveClaimAmount).toBe(signedAmount)

const balanceBeforeClaim = await readMockErc20BalanceOf(defaultOptions.config, {
address: asset,
args: [claimant],
})

await fixtures.core.claimIncentiveFor(
boost.id,
0n,
referrer,
claimDataPayload,
claimant,
);

const balanceAfterClaim = await readMockErc20BalanceOf(defaultOptions.config, {
address: asset,
args: [claimant],
})

expect(balanceAfterClaim - balanceBeforeClaim).toBe(signedAmount);
})
});
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import {
decodeAbiParameters,
decodeFunctionData,
encodeAbiParameters,
parseEther,
parseEventLogs,
zeroAddress,
} from 'viem';
Expand Down Expand Up @@ -651,27 +652,108 @@ export class ERC20PeggedVariableCriteriaIncentive extends DeployableTarget<
* Builds the claim data for the ERC20PeggedVariableCriteriaIncentivePayload.
*
* @public
* @param {bigint} rewardAmount
* @param {bigint} signedAmount
* @returns {Hash} Returns the encoded claim data
* @description This function returns the encoded claim data for the ERC20PeggedVariableCriteriaIncentivePayload.
*/
public buildClaimData(rewardAmount: bigint) {
public buildClaimData(signedAmount: bigint) {
return encodeAbiParameters(
[{ type: 'uint256', name: 'rewardAmount' }],
[rewardAmount],
[{ type: 'uint256', name: 'signedAmount' }],
[signedAmount],
);
}

/**
* Decodes claim data for the ERC20PeggedVariableCriteriaIncentive, returning the encoded claim amount.
* Decodes claim data for the ERC20PeggedVariableCriteriaIncentive, returning the claim amount.
* Useful when deriving amount claimed from logs.
*
* @public
* @param {Hex} claimData
* @returns {BigInt} Returns the reward amount from a claim data payload
*/
public decodeClaimData(data: Hex) {
return BigInt(decodeAbiParameters([{ type: 'uint256' }], data)[0]);
public async decodeClaimData(claimData: Hex) {
const boostClaimData = decodeAbiParameters(
[
{
type: 'tuple',
name: 'BoostClaimData',
components: [
{ type: 'bytes', name: 'validatorData' },
{ type: 'bytes', name: 'incentiveData' },
],
},
],
claimData,
);
const signedAmount = decodeAbiParameters(
[{ type: 'uint256' }],
boostClaimData[0].incentiveData,
)[0];
let claimAmount = signedAmount;
const [reward, maxReward] = await Promise.all([
this.reward(),
this.getMaxReward(),
]);

if (reward === 0n) {
return claimAmount;
} else {
claimAmount = (reward * signedAmount) / parseEther('1');
}

if (maxReward !== 0n && claimAmount > maxReward) {
claimAmount = maxReward;
}

return claimAmount;
}

/**
* Decodes claim data for the ERC20PeggedVariableCriteriaIncentive, returning the claim amount.
* Useful when deriving amount claimed from logs.
* Use this function instead of `decodeClaimData` if you have reward details.
*
* @public
* @param {Hex} claimData
* @param {bigint} [reward]
* @param {bigint} [maxReward]
* @returns {BigInt} Returns the reward amount from a claim data payload
*/
public decodeClaimDataWithRewardDetails(
claimData: Hex,
reward: bigint,
maxReward: bigint,
) {
const boostClaimData = decodeAbiParameters(
[
{
type: 'tuple',
name: 'BoostClaimData',
components: [
{ type: 'bytes', name: 'validatorData' },
{ type: 'bytes', name: 'incentiveData' },
],
},
],
claimData,
);
const signedAmount = decodeAbiParameters(
[{ type: 'uint256' }],
boostClaimData[0].incentiveData,
)[0];
let claimAmount = signedAmount;

if (reward === 0n) {
return claimAmount;
} else {
claimAmount = (reward * signedAmount) / parseEther('1');
}

if (maxReward !== 0n && claimAmount > maxReward) {
claimAmount = maxReward;
}

return claimAmount;
}
}

Expand Down

0 comments on commit f01db75

Please sign in to comment.