From 2b3d6ce7418a96d4c793a306f89f6cec796daf12 Mon Sep 17 00:00:00 2001 From: mmackz Date: Sat, 10 May 2025 17:08:28 -0700 Subject: [PATCH 1/5] fix(Incentive): fix variable criteria incentive type detection --- packages/sdk/src/Incentives/Incentive.ts | 35 ++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/packages/sdk/src/Incentives/Incentive.ts b/packages/sdk/src/Incentives/Incentive.ts index a63477710..657caa947 100644 --- a/packages/sdk/src/Incentives/Incentive.ts +++ b/packages/sdk/src/Incentives/Incentive.ts @@ -1,4 +1,8 @@ -import { aIncentiveAbi } from '@boostxyz/evm'; +import { + aIncentiveAbi, + readErc20PeggedVariableCriteriaIncentiveV2GetIncentiveCriteria, + readErc20VariableCriteriaIncentiveV2GetIncentiveCriteria, +} from '@boostxyz/evm'; import { AAllowListIncentive, ACGDAIncentive, @@ -9,7 +13,6 @@ import { AERC20VariableCriteriaIncentive, AERC20VariableCriteriaIncentiveV2, AERC20VariableIncentive, - // AERC20VariableCriteriaIncentive APointsIncentive, } from '@boostxyz/evm/deploys/componentInterfaces.json'; import { readContract } from '@wagmi/core'; @@ -111,5 +114,33 @@ export async function incentiveFromAddress( interfaceId as Hex, ); } + + /* + * Because the interfaceId is identical for V1 and V2 variable criteria incentive types, + * a V2-specific read is performed. This read is expected to succeed only for V2 contracts, + * allowing for selection of the correct SDK constructor. + */ + if (interfaceId === AERC20VariableCriteriaIncentive) { + try { + await readErc20VariableCriteriaIncentiveV2GetIncentiveCriteria( + options.config, + { address, ...params }, + ); + return new ERC20VariableCriteriaIncentiveV2(options, address); + } catch { + return new ERC20VariableCriteriaIncentive(options, address); + } + } else if (interfaceId === AERC20PeggedVariableCriteriaIncentive) { + try { + await readErc20PeggedVariableCriteriaIncentiveV2GetIncentiveCriteria( + options.config, + { address, ...params }, + ); + return new ERC20PeggedVariableCriteriaIncentiveV2(options, address); + } catch { + return new ERC20PeggedVariableCriteriaIncentive(options, address); + } + } + return new Ctor(options, address); } From 8c1b2b4b0b122376f1c58262bd28ff0d4229d168 Mon Sep 17 00:00:00 2001 From: mmackz Date: Sat, 10 May 2025 17:43:16 -0700 Subject: [PATCH 2/5] test(incentive): add tests for incentiveFromAddress --- packages/sdk/src/Incentives/Incentive.test.ts | 145 +++++++++++++++++- 1 file changed, 144 insertions(+), 1 deletion(-) diff --git a/packages/sdk/src/Incentives/Incentive.test.ts b/packages/sdk/src/Incentives/Incentive.test.ts index 0df3935e0..431115861 100644 --- a/packages/sdk/src/Incentives/Incentive.test.ts +++ b/packages/sdk/src/Incentives/Incentive.test.ts @@ -1,4 +1,4 @@ -import { zeroAddress } from 'viem'; +import { pad, zeroAddress } from 'viem'; import { describe, expect, test } from 'vitest'; import { defaultOptions } from '@boostxyz/test/helpers'; import { StrategyType } from '../claiming'; @@ -8,8 +8,14 @@ import { ERC20Incentive, ERC20VariableIncentive, incentiveFromAddress, + ERC20VariableCriteriaIncentive, + ERC20VariableCriteriaIncentiveV2, + ERC20PeggedVariableCriteriaIncentive, + ERC20PeggedVariableCriteriaIncentiveV2, + ERC20PeggedIncentive, } from './Incentive'; import { PointsIncentive } from './PointsIncentive'; +import { SignatureType, ValueType } from '../Actions/EventAction'; describe('Incentive', () => { test('can automatically instantiate PointsIncentive given an address', async () => { @@ -19,6 +25,7 @@ describe('Incentive', () => { reward: 1n, limit: 1n, }); + // @ts-expect-error private method await incentive.deploy(); expect( await incentiveFromAddress( @@ -33,6 +40,7 @@ describe('Incentive', () => { allowList: zeroAddress, limit: 3n, }); + // @ts-expect-error private method await incentive.deploy(); expect( await incentiveFromAddress( @@ -51,6 +59,7 @@ describe('Incentive', () => { rewardDecay: 1n, manager: zeroAddress, }); + // @ts-expect-error private method await incentive.deploy(); expect( await incentiveFromAddress( @@ -68,6 +77,7 @@ describe('Incentive', () => { limit: 10n, manager: zeroAddress, }); + // @ts-expect-error private method await incentive.deploy(); expect( await incentiveFromAddress( @@ -84,6 +94,7 @@ describe('Incentive', () => { limit: 10n, manager: zeroAddress, }); + // @ts-expect-error private method await incentive.deploy(); expect( await incentiveFromAddress( @@ -92,4 +103,136 @@ describe('Incentive', () => { ), ).toBeInstanceOf(ERC20VariableIncentive); }); + + test('can automatically instantiate ERC20VariableCriteriaIncentive (V1) given an address', async () => { + const incentive = new ERC20VariableCriteriaIncentive(defaultOptions, { + asset: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', + limit: 1000000n, + reward: 1000000n, + criteria: { + criteriaType: SignatureType.FUNC, + signature: + pad("0xa9059cbb"), + fieldIndex: 1, + targetContract: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", + }, + }); + // @ts-expect-error private method + await incentive.deploy(); + expect( + await incentiveFromAddress( + defaultOptions, + incentive.assertValidAddress(), + ), + ).toBeInstanceOf(ERC20VariableCriteriaIncentive); + }); + + test('can automatically instantiate ERC20VariableCriteriaIncentiveV2 given an address', async () => { + const incentive = new ERC20VariableCriteriaIncentiveV2(defaultOptions, { + asset: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', + limit: 1000000n, + reward: 1000000n, + criteria: { + criteriaType: SignatureType.FUNC, + signature: + pad("0xa9059cbb"), + fieldIndex: 1, + targetContract: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", + valueType: ValueType.WAD, + }, + }); + // @ts-expect-error private method + await incentive.deploy(); + expect( + await incentiveFromAddress( + defaultOptions, + incentive.assertValidAddress(), + ), + ).toBeInstanceOf(ERC20VariableCriteriaIncentiveV2); + }); + + test('can automatically instantiate ERC20PeggedVariableCriteriaIncentive (V1) given an address', async () => { + const incentive = new ERC20PeggedVariableCriteriaIncentive(defaultOptions, { + asset: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', + peg: zeroAddress, + limit: 1000000n, + reward: 1000000n, + maxReward: 1000000n, + criteria: { + criteriaType: SignatureType.FUNC, + signature: + pad("0xa9059cbb"), + fieldIndex: 1, + targetContract: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", + }, + }); + // @ts-expect-error private method + await incentive.deploy(); + expect( + await incentiveFromAddress( + defaultOptions, + incentive.assertValidAddress(), + ), + ).toBeInstanceOf(ERC20PeggedVariableCriteriaIncentive); + }); + + test('can automatically instantiate ERC20PeggedVariableCriteriaIncentiveV2 given an address', async () => { + const incentive = new ERC20PeggedVariableCriteriaIncentiveV2(defaultOptions, { + asset: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', + peg: zeroAddress, + limit: 1000000n, + reward: 1000000n, + maxReward: 1000000n, + criteria: { + criteriaType: SignatureType.FUNC, + signature: + pad("0xa9059cbb"), + fieldIndex: 1, + targetContract: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", + valueType: ValueType.WAD, + }, + }); + // @ts-expect-error private method + await incentive.deploy(); + expect( + await incentiveFromAddress( + defaultOptions, + incentive.assertValidAddress(), + ), + ).toBeInstanceOf(ERC20PeggedVariableCriteriaIncentiveV2); + }); + + test('can automatically instantiate ERC20Incentive given an address', async () => { + const incentive = new ERC20Incentive(defaultOptions, { + asset: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', + limit: 1000000n, + reward: 100000n, + strategy: StrategyType.POOL, + }); + // @ts-expect-error private method + await incentive.deploy(); + expect( + await incentiveFromAddress( + defaultOptions, + incentive.assertValidAddress(), + ), + ).toBeInstanceOf(ERC20Incentive); + }); + + test('can automatically instantiate ERC20PeggedIncentive given an address', async () => { + const incentive = new ERC20PeggedIncentive(defaultOptions, { + asset: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', + peg: zeroAddress, + limit: 1000000n, + reward: 100000n, + }); + // @ts-expect-error private method + await incentive.deploy(); + expect( + await incentiveFromAddress( + defaultOptions, + incentive.assertValidAddress(), + ), + ).toBeInstanceOf(ERC20PeggedIncentive); + }); }); From 62d994a311ce5e539f3fe0cd8faaad7bd55dd5b6 Mon Sep 17 00:00:00 2001 From: mmackz Date: Sat, 10 May 2025 17:45:19 -0700 Subject: [PATCH 3/5] chore: generate changeset --- .changeset/odd-pumpkins-notice.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/odd-pumpkins-notice.md diff --git a/.changeset/odd-pumpkins-notice.md b/.changeset/odd-pumpkins-notice.md new file mode 100644 index 000000000..73057c104 --- /dev/null +++ b/.changeset/odd-pumpkins-notice.md @@ -0,0 +1,5 @@ +--- +"@boostxyz/sdk": patch +--- + +improve detection of variable criteria incentive types From 60fd09a66328513b78f9b994d57cf3fa29f52d28 Mon Sep 17 00:00:00 2001 From: mmackz Date: Sat, 10 May 2025 17:58:19 -0700 Subject: [PATCH 4/5] chore: format --- packages/sdk/src/Incentives/Incentive.test.ts | 53 +++++++------------ 1 file changed, 18 insertions(+), 35 deletions(-) diff --git a/packages/sdk/src/Incentives/Incentive.test.ts b/packages/sdk/src/Incentives/Incentive.test.ts index 431115861..2c032b9e7 100644 --- a/packages/sdk/src/Incentives/Incentive.test.ts +++ b/packages/sdk/src/Incentives/Incentive.test.ts @@ -7,12 +7,12 @@ import { CGDAIncentive, ERC20Incentive, ERC20VariableIncentive, - incentiveFromAddress, ERC20VariableCriteriaIncentive, ERC20VariableCriteriaIncentiveV2, ERC20PeggedVariableCriteriaIncentive, ERC20PeggedVariableCriteriaIncentiveV2, ERC20PeggedIncentive, + incentiveFromAddress, } from './Incentive'; import { PointsIncentive } from './PointsIncentive'; import { SignatureType, ValueType } from '../Actions/EventAction'; @@ -104,6 +104,23 @@ describe('Incentive', () => { ).toBeInstanceOf(ERC20VariableIncentive); }); + test('can automatically instantiate ERC20PeggedIncentive given an address', async () => { + const incentive = new ERC20PeggedIncentive(defaultOptions, { + asset: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', + peg: zeroAddress, + limit: 1000000n, + reward: 100000n, + }); + // @ts-expect-error private method + await incentive.deploy(); + expect( + await incentiveFromAddress( + defaultOptions, + incentive.assertValidAddress(), + ), + ).toBeInstanceOf(ERC20PeggedIncentive); + }); + test('can automatically instantiate ERC20VariableCriteriaIncentive (V1) given an address', async () => { const incentive = new ERC20VariableCriteriaIncentive(defaultOptions, { asset: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', @@ -201,38 +218,4 @@ describe('Incentive', () => { ), ).toBeInstanceOf(ERC20PeggedVariableCriteriaIncentiveV2); }); - - test('can automatically instantiate ERC20Incentive given an address', async () => { - const incentive = new ERC20Incentive(defaultOptions, { - asset: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', - limit: 1000000n, - reward: 100000n, - strategy: StrategyType.POOL, - }); - // @ts-expect-error private method - await incentive.deploy(); - expect( - await incentiveFromAddress( - defaultOptions, - incentive.assertValidAddress(), - ), - ).toBeInstanceOf(ERC20Incentive); - }); - - test('can automatically instantiate ERC20PeggedIncentive given an address', async () => { - const incentive = new ERC20PeggedIncentive(defaultOptions, { - asset: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', - peg: zeroAddress, - limit: 1000000n, - reward: 100000n, - }); - // @ts-expect-error private method - await incentive.deploy(); - expect( - await incentiveFromAddress( - defaultOptions, - incentive.assertValidAddress(), - ), - ).toBeInstanceOf(ERC20PeggedIncentive); - }); }); From 038fd649ca8f0e1595b73814b5c11d7647223670 Mon Sep 17 00:00:00 2001 From: mmackz Date: Sun, 11 May 2025 15:11:02 -0700 Subject: [PATCH 5/5] refactor(Incentive): reorder incentive entries for clarity --- packages/sdk/src/Incentives/Incentive.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/sdk/src/Incentives/Incentive.ts b/packages/sdk/src/Incentives/Incentive.ts index 657caa947..e9e04b25c 100644 --- a/packages/sdk/src/Incentives/Incentive.ts +++ b/packages/sdk/src/Incentives/Incentive.ts @@ -82,8 +82,8 @@ export const IncentiveByComponentInterface = { // [AERC1155Incentive as Hex]: ERC1155Incentive, [ACGDAIncentive as Hex]: CGDAIncentive, [AERC20VariableIncentive as Hex]: ERC20VariableIncentive, - [AERC20VariableCriteriaIncentiveV2 as Hex]: ERC20VariableCriteriaIncentiveV2, [AERC20VariableCriteriaIncentive as Hex]: ERC20VariableCriteriaIncentive, + [AERC20VariableCriteriaIncentiveV2 as Hex]: ERC20VariableCriteriaIncentiveV2, }; /**