@boostxyz/sdk 5.2.0 → 5.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -0
- package/dist/Actions/Action.cjs +1 -1
- package/dist/Actions/Action.js +1 -1
- package/dist/Actions/EventAction.cjs +1 -1
- package/dist/Actions/EventAction.cjs.map +1 -1
- package/dist/Actions/EventAction.d.ts +82 -38
- package/dist/Actions/EventAction.d.ts.map +1 -1
- package/dist/Actions/EventAction.js +437 -297
- package/dist/Actions/EventAction.js.map +1 -1
- package/dist/AllowLists/AllowList.cjs +1 -1
- package/dist/AllowLists/AllowList.js +2 -2
- package/dist/AllowLists/SimpleAllowList.cjs +1 -1
- package/dist/AllowLists/SimpleAllowList.js +2 -2
- package/dist/AllowLists/SimpleDenyList.cjs +1 -1
- package/dist/AllowLists/SimpleDenyList.js +3 -3
- package/dist/Auth/PassthroughAuth.cjs +1 -1
- package/dist/Auth/PassthroughAuth.js +1 -1
- package/dist/BoostCore.cjs +2 -2
- package/dist/BoostCore.cjs.map +1 -1
- package/dist/BoostCore.d.ts +42 -1
- package/dist/BoostCore.d.ts.map +1 -1
- package/dist/BoostCore.js +360 -318
- package/dist/BoostCore.js.map +1 -1
- package/dist/BoostRegistry.cjs +1 -1
- package/dist/BoostRegistry.js +2 -2
- package/dist/{Budget-N0YEfSt2.cjs → Budget-AoNx7uFd.cjs} +2 -2
- package/dist/{Budget-N0YEfSt2.cjs.map → Budget-AoNx7uFd.cjs.map} +1 -1
- package/dist/{Budget-C0SMvfEl.js → Budget-DYIV9iNK.js} +3 -3
- package/dist/{Budget-C0SMvfEl.js.map → Budget-DYIV9iNK.js.map} +1 -1
- package/dist/Budgets/Budget.cjs +1 -1
- package/dist/Budgets/Budget.js +2 -2
- package/dist/Budgets/ManagedBudget.cjs +1 -1
- package/dist/Budgets/ManagedBudget.js +2 -2
- package/dist/Deployable/DeployableTarget.cjs +1 -1
- package/dist/Deployable/DeployableTarget.js +1 -1
- package/dist/Deployable/DeployableTargetWithRBAC.cjs +1 -1
- package/dist/Deployable/DeployableTargetWithRBAC.js +2 -2
- package/dist/Incentive-BbkfwGOb.cjs +2 -0
- package/dist/Incentive-BbkfwGOb.cjs.map +1 -0
- package/dist/{Incentive-BwRH0A6j.js → Incentive-qlnv5kQB.js} +203 -124
- package/dist/Incentive-qlnv5kQB.js.map +1 -0
- package/dist/Incentives/AllowListIncentive.cjs +1 -1
- package/dist/Incentives/AllowListIncentive.js +3 -3
- package/dist/Incentives/CGDAIncentive.cjs +1 -1
- package/dist/Incentives/CGDAIncentive.js +2 -2
- package/dist/Incentives/ERC20Incentive.cjs +1 -1
- package/dist/Incentives/ERC20Incentive.js +6 -6
- package/dist/Incentives/ERC20PeggedIncentive.d.ts +10 -1
- package/dist/Incentives/ERC20PeggedIncentive.d.ts.map +1 -1
- package/dist/Incentives/ERC20PeggedVariableCriteriaIncentive.d.ts +16 -4
- package/dist/Incentives/ERC20PeggedVariableCriteriaIncentive.d.ts.map +1 -1
- package/dist/Incentives/ERC20VariableCriteriaIncentive.cjs +1 -1
- package/dist/Incentives/ERC20VariableCriteriaIncentive.js +2 -2
- package/dist/Incentives/ERC20VariableIncentive.cjs +1 -1
- package/dist/Incentives/ERC20VariableIncentive.js +2 -2
- package/dist/Incentives/Incentive.cjs +1 -1
- package/dist/Incentives/Incentive.js +2 -2
- package/dist/Incentives/PointsIncentive.cjs +1 -1
- package/dist/Incentives/PointsIncentive.js +2 -2
- package/dist/{SimpleDenyList-B8QeJthf.js → SimpleDenyList-ByAr4X1r.js} +3 -3
- package/dist/{SimpleDenyList-B8QeJthf.js.map → SimpleDenyList-ByAr4X1r.js.map} +1 -1
- package/dist/{SimpleDenyList-DIb4xX3j.cjs → SimpleDenyList-CsRXJPwm.cjs} +2 -2
- package/dist/{SimpleDenyList-DIb4xX3j.cjs.map → SimpleDenyList-CsRXJPwm.cjs.map} +1 -1
- package/dist/Validators/LimitedSignerValidator.cjs +1 -1
- package/dist/Validators/LimitedSignerValidator.js +2 -2
- package/dist/Validators/SignerValidator.cjs +1 -1
- package/dist/Validators/SignerValidator.js +2 -2
- package/dist/Validators/Validator.cjs +1 -1
- package/dist/Validators/Validator.js +1 -1
- package/dist/{deployments-COxshLqt.js → deployments-D0fs26TV.js} +16 -16
- package/dist/{deployments-COxshLqt.js.map → deployments-D0fs26TV.js.map} +1 -1
- package/dist/{deployments-BGpr4ppG.cjs → deployments-DoIOqxco.cjs} +2 -2
- package/dist/deployments-DoIOqxco.cjs.map +1 -0
- package/dist/deployments.json +3 -3
- package/dist/errors.cjs +1 -1
- package/dist/errors.cjs.map +1 -1
- package/dist/errors.d.ts +40 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +42 -16
- package/dist/errors.js.map +1 -1
- package/dist/{generated-ClbO_ULI.js → generated-Cyvr_Tjx.js} +446 -438
- package/dist/generated-Cyvr_Tjx.js.map +1 -0
- package/dist/{generated-CRD9XfOT.cjs → generated-DtYPHhtX.cjs} +2 -2
- package/dist/generated-DtYPHhtX.cjs.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.js +160 -155
- package/dist/utils.cjs +1 -1
- package/dist/utils.cjs.map +1 -1
- package/dist/utils.js +367 -19
- package/dist/utils.js.map +1 -1
- package/package.json +1 -1
- package/src/Actions/EventAction.test.ts +285 -3
- package/src/Actions/EventAction.ts +402 -124
- package/src/BoostCore.test.ts +51 -3
- package/src/BoostCore.ts +73 -0
- package/src/Incentives/ERC20PeggedIncentive.ts +33 -4
- package/src/Incentives/ERC20PeggedVariableCriteriaIncentive.test.ts +67 -25
- package/src/Incentives/ERC20PeggedVariableCriteriaIncentive.ts +89 -7
- package/src/errors.ts +50 -0
- package/dist/Incentive-BwRH0A6j.js.map +0 -1
- package/dist/Incentive-CZw_hu64.cjs +0 -2
- package/dist/Incentive-CZw_hu64.cjs.map +0 -1
- package/dist/deployments-BGpr4ppG.cjs.map +0 -1
- package/dist/generated-CRD9XfOT.cjs.map +0 -1
- package/dist/generated-ClbO_ULI.js.map +0 -1
package/src/BoostCore.test.ts
CHANGED
|
@@ -17,6 +17,8 @@ import { BoostNotFoundError, IncentiveNotCloneableError } from "./errors";
|
|
|
17
17
|
import type { ERC20Incentive } from "./Incentives/ERC20Incentive";
|
|
18
18
|
import { bytes4 } from "./utils";
|
|
19
19
|
import { BoostValidatorEOA } from "./Validators/Validator";
|
|
20
|
+
import { AssetType } from "./transfers";
|
|
21
|
+
import { waitForTransactionReceipt } from "@wagmi/core";
|
|
20
22
|
|
|
21
23
|
let fixtures: Fixtures, budgets: BudgetFixtures;
|
|
22
24
|
|
|
@@ -32,7 +34,7 @@ describe("BoostCore", () => {
|
|
|
32
34
|
const { core } = fixtures;
|
|
33
35
|
|
|
34
36
|
const { budget, erc20 } = budgets;
|
|
35
|
-
|
|
37
|
+
const payload = {
|
|
36
38
|
protocolFee: 0n,
|
|
37
39
|
maxParticipants: 5n,
|
|
38
40
|
budget: budget,
|
|
@@ -59,8 +61,11 @@ describe("BoostCore", () => {
|
|
|
59
61
|
manager: budget.assertValidAddress(),
|
|
60
62
|
}),
|
|
61
63
|
],
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
+
}
|
|
65
|
+
const { hash } = await core.createBoostRaw(payload)
|
|
66
|
+
await waitForTransactionReceipt(defaultOptions.config, { hash })
|
|
67
|
+
await core.createBoost(payload);
|
|
68
|
+
expect(await core.getBoostCount()).toBe(2n);
|
|
64
69
|
});
|
|
65
70
|
|
|
66
71
|
test("throws a typed error if no boost exists", async () => {
|
|
@@ -904,4 +909,47 @@ describe("BoostCore", () => {
|
|
|
904
909
|
)
|
|
905
910
|
expect(totalFee).toBe(100000000000000000n)
|
|
906
911
|
});
|
|
912
|
+
|
|
913
|
+
test("can get incentive fees information", async () => {
|
|
914
|
+
const { core } = fixtures;
|
|
915
|
+
const { budget, erc20 } = budgets;
|
|
916
|
+
|
|
917
|
+
// Create a new boost
|
|
918
|
+
const boost = await core.createBoost({
|
|
919
|
+
protocolFee: 0n,
|
|
920
|
+
maxParticipants: 10n,
|
|
921
|
+
budget: budget,
|
|
922
|
+
action: core.EventAction(
|
|
923
|
+
makeMockEventActionPayload(
|
|
924
|
+
core.assertValidAddress(),
|
|
925
|
+
erc20.assertValidAddress(),
|
|
926
|
+
),
|
|
927
|
+
),
|
|
928
|
+
validator: core.SignerValidator({
|
|
929
|
+
signers: [defaultOptions.account.address],
|
|
930
|
+
validatorCaller: defaultOptions.account.address,
|
|
931
|
+
}),
|
|
932
|
+
allowList: core.SimpleAllowList({
|
|
933
|
+
owner: defaultOptions.account.address,
|
|
934
|
+
allowed: [defaultOptions.account.address],
|
|
935
|
+
}),
|
|
936
|
+
incentives: [
|
|
937
|
+
core.ERC20Incentive({
|
|
938
|
+
asset: erc20.assertValidAddress(),
|
|
939
|
+
reward: parseEther("1"),
|
|
940
|
+
limit: 10n,
|
|
941
|
+
strategy: StrategyType.POOL,
|
|
942
|
+
manager: budget.assertValidAddress(),
|
|
943
|
+
}),
|
|
944
|
+
],
|
|
945
|
+
});
|
|
946
|
+
|
|
947
|
+
const feesInfo = await core.getIncentiveFeesInfo(boost.id, 0n);
|
|
948
|
+
|
|
949
|
+
expect(feesInfo).toBeDefined();
|
|
950
|
+
expect(feesInfo.protocolFee).toBe(1000n);
|
|
951
|
+
expect(feesInfo.protocolFeesRemaining).toBe(parseEther("1"));
|
|
952
|
+
expect(feesInfo.assetType).toBe(AssetType.ERC20);
|
|
953
|
+
expect(feesInfo.asset.toLowerCase()).toBe(erc20.assertValidAddress());
|
|
954
|
+
});
|
|
907
955
|
});
|
package/src/BoostCore.ts
CHANGED
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
readBoostCoreCreateBoostAuth,
|
|
4
4
|
readBoostCoreGetBoost,
|
|
5
5
|
readBoostCoreGetBoostCount,
|
|
6
|
+
readBoostCoreGetIncentiveFeesInfo,
|
|
6
7
|
readBoostCoreProtocolFee,
|
|
7
8
|
readBoostCoreProtocolFeeReceiver,
|
|
8
9
|
readIAuthIsAuthorized,
|
|
@@ -13,6 +14,7 @@ import {
|
|
|
13
14
|
simulateBoostCoreSetProtocolFeeReceiver,
|
|
14
15
|
writeBoostCoreClaimIncentive,
|
|
15
16
|
writeBoostCoreClaimIncentiveFor,
|
|
17
|
+
writeBoostCoreCreateBoost,
|
|
16
18
|
writeBoostCoreSetCreateBoostAuth,
|
|
17
19
|
writeBoostCoreSetProtocolFeeReceiver,
|
|
18
20
|
} from '@boostxyz/evm';
|
|
@@ -29,6 +31,8 @@ import {
|
|
|
29
31
|
type Address,
|
|
30
32
|
type ContractEventName,
|
|
31
33
|
type Hex,
|
|
34
|
+
encodePacked,
|
|
35
|
+
keccak256,
|
|
32
36
|
parseEventLogs,
|
|
33
37
|
zeroAddress,
|
|
34
38
|
zeroHash,
|
|
@@ -126,8 +130,10 @@ import {
|
|
|
126
130
|
InvalidProtocolChainIdError,
|
|
127
131
|
MustInitializeBudgetError,
|
|
128
132
|
} from './errors';
|
|
133
|
+
import type { AssetType } from './transfers';
|
|
129
134
|
import {
|
|
130
135
|
type GenericLog,
|
|
136
|
+
type HashAndSimulatedResult,
|
|
131
137
|
type ReadParams,
|
|
132
138
|
type WriteParams,
|
|
133
139
|
assertValidAddressByChainId,
|
|
@@ -275,6 +281,20 @@ export type CreateBoostPayload = {
|
|
|
275
281
|
owner?: Address;
|
|
276
282
|
};
|
|
277
283
|
|
|
284
|
+
/**
|
|
285
|
+
* Represents the information about the disbursal of an incentive.
|
|
286
|
+
*
|
|
287
|
+
* @export
|
|
288
|
+
* @typedef {IncentiveDisbursalInfo}
|
|
289
|
+
*/
|
|
290
|
+
export type IncentiveDisbursalInfo = {
|
|
291
|
+
assetType: AssetType;
|
|
292
|
+
asset: Address;
|
|
293
|
+
protocolFeesRemaining: bigint;
|
|
294
|
+
protocolFee: bigint;
|
|
295
|
+
tokenId: bigint;
|
|
296
|
+
};
|
|
297
|
+
|
|
278
298
|
/**
|
|
279
299
|
* The core contract for the Boost protocol. Used to create and retrieve deployed Boosts.
|
|
280
300
|
*
|
|
@@ -407,6 +427,27 @@ export class BoostCore extends Deployable<
|
|
|
407
427
|
});
|
|
408
428
|
}
|
|
409
429
|
|
|
430
|
+
/**
|
|
431
|
+
* Create a new Boost.
|
|
432
|
+
*
|
|
433
|
+
* @public
|
|
434
|
+
* @async
|
|
435
|
+
* @param {CreateBoostPayload} _boostPayload
|
|
436
|
+
* @param {?WriteParams} [params]
|
|
437
|
+
* @returns {Promise<HashAndSimulatedResult>}
|
|
438
|
+
*/
|
|
439
|
+
public async createBoostRaw(
|
|
440
|
+
_boostPayload: CreateBoostPayload,
|
|
441
|
+
_params?: WriteParams,
|
|
442
|
+
): Promise<HashAndSimulatedResult> {
|
|
443
|
+
const { request, result } = await this.simulateCreateBoost(
|
|
444
|
+
_boostPayload,
|
|
445
|
+
_params,
|
|
446
|
+
);
|
|
447
|
+
const hash = await writeBoostCoreCreateBoost(this._config, request);
|
|
448
|
+
return { hash, result };
|
|
449
|
+
}
|
|
450
|
+
|
|
410
451
|
/**
|
|
411
452
|
* Returns a simulated Boost creation.
|
|
412
453
|
*
|
|
@@ -1046,6 +1087,38 @@ export class BoostCore extends Deployable<
|
|
|
1046
1087
|
return { hash, result };
|
|
1047
1088
|
}
|
|
1048
1089
|
|
|
1090
|
+
/**
|
|
1091
|
+
* Get the incentives fees information for a given Boost ID and Incentive ID.
|
|
1092
|
+
*
|
|
1093
|
+
* @public
|
|
1094
|
+
* @async
|
|
1095
|
+
* @param {bigint} boostId - The ID of the Boost
|
|
1096
|
+
* @param {bigint} incentiveId - The ID of the Incentive
|
|
1097
|
+
* @param {?ReadParams} [params]
|
|
1098
|
+
* @returns {Promise<IncentiveDisbursalInfo>}
|
|
1099
|
+
*/
|
|
1100
|
+
public async getIncentiveFeesInfo(
|
|
1101
|
+
boostId: bigint,
|
|
1102
|
+
incentiveId: bigint,
|
|
1103
|
+
params?: ReadParams,
|
|
1104
|
+
) {
|
|
1105
|
+
const key = keccak256(
|
|
1106
|
+
encodePacked(['uint256', 'uint256'], [boostId, incentiveId]),
|
|
1107
|
+
);
|
|
1108
|
+
|
|
1109
|
+
return await readBoostCoreGetIncentiveFeesInfo(this._config, {
|
|
1110
|
+
...assertValidAddressByChainId(
|
|
1111
|
+
this._config,
|
|
1112
|
+
this.addresses,
|
|
1113
|
+
params?.chainId,
|
|
1114
|
+
),
|
|
1115
|
+
args: [key],
|
|
1116
|
+
...this.optionallyAttachAccount(),
|
|
1117
|
+
// biome-ignore lint/suspicious/noExplicitAny: Accept any shape of valid wagmi/viem parameters, wagmi does the same thing internally
|
|
1118
|
+
...(params as any),
|
|
1119
|
+
});
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1049
1122
|
/**
|
|
1050
1123
|
* Retrieves the claim information from a transaction receipt.
|
|
1051
1124
|
*
|
|
@@ -21,9 +21,9 @@ import {
|
|
|
21
21
|
type Address,
|
|
22
22
|
type ContractEventName,
|
|
23
23
|
type Hex,
|
|
24
|
+
decodeAbiParameters,
|
|
24
25
|
encodeAbiParameters,
|
|
25
26
|
zeroAddress,
|
|
26
|
-
zeroHash,
|
|
27
27
|
} from 'viem';
|
|
28
28
|
import { ERC20PeggedIncentive as ERC20PeggedIncentiveBases } from '../../dist/deployments.json';
|
|
29
29
|
import type {
|
|
@@ -422,11 +422,11 @@ export class ERC20PeggedIncentive extends DeployableTarget<
|
|
|
422
422
|
* @returns {Promise<bigint>} - True if total claims is less than limit
|
|
423
423
|
*/
|
|
424
424
|
public async getRemainingClaimPotential(params?: ReadParams) {
|
|
425
|
-
const [
|
|
426
|
-
this.
|
|
425
|
+
const [totalClaimed, limit] = await Promise.all([
|
|
426
|
+
this.totalClaimed(params),
|
|
427
427
|
this.limit(params),
|
|
428
428
|
]);
|
|
429
|
-
return limit -
|
|
429
|
+
return limit - totalClaimed;
|
|
430
430
|
}
|
|
431
431
|
|
|
432
432
|
/**
|
|
@@ -478,6 +478,35 @@ export class ERC20PeggedIncentive extends DeployableTarget<
|
|
|
478
478
|
[rewardAmount],
|
|
479
479
|
);
|
|
480
480
|
}
|
|
481
|
+
|
|
482
|
+
/**
|
|
483
|
+
* Decodes claim data for the ERC20PeggedIncentive, returning the claim amount.
|
|
484
|
+
* Useful when deriving amount claimed from logs.
|
|
485
|
+
*
|
|
486
|
+
* @public
|
|
487
|
+
* @param {Hex} claimData
|
|
488
|
+
* @returns {BigInt} Returns the reward amount from a claim data payload
|
|
489
|
+
*/
|
|
490
|
+
public decodeClaimData(claimData: Hex) {
|
|
491
|
+
const boostClaimData = decodeAbiParameters(
|
|
492
|
+
[
|
|
493
|
+
{
|
|
494
|
+
type: 'tuple',
|
|
495
|
+
name: 'BoostClaimData',
|
|
496
|
+
components: [
|
|
497
|
+
{ type: 'bytes', name: 'validatorData' },
|
|
498
|
+
{ type: 'bytes', name: 'incentiveData' },
|
|
499
|
+
],
|
|
500
|
+
},
|
|
501
|
+
],
|
|
502
|
+
claimData,
|
|
503
|
+
);
|
|
504
|
+
const signedAmount = decodeAbiParameters(
|
|
505
|
+
[{ type: 'uint256' }],
|
|
506
|
+
boostClaimData[0].incentiveData,
|
|
507
|
+
)[0];
|
|
508
|
+
return signedAmount;
|
|
509
|
+
}
|
|
481
510
|
}
|
|
482
511
|
|
|
483
512
|
/**
|
|
@@ -33,6 +33,7 @@ import {
|
|
|
33
33
|
gasRebateIncentiveCriteria,
|
|
34
34
|
} from "./ERC20VariableCriteriaIncentive";
|
|
35
35
|
import { allKnownSignatures } from "@boostxyz/test/allKnownSignatures";
|
|
36
|
+
import { readMockErc20BalanceOf } from "@boostxyz/evm";
|
|
36
37
|
|
|
37
38
|
/**
|
|
38
39
|
* A basic ERC721 mint scalar criteria for testing
|
|
@@ -73,11 +74,11 @@ export function basicErc721MintScalarCriteria(
|
|
|
73
74
|
let fixtures: Fixtures,
|
|
74
75
|
erc20: MockERC20,
|
|
75
76
|
erc721: MockERC721,
|
|
76
|
-
|
|
77
|
+
erc20PeggedVariableCriteriaIncentive: ERC20PeggedVariableCriteriaIncentive,
|
|
77
78
|
budgets: BudgetFixtures,
|
|
78
79
|
boost: Boost;
|
|
79
80
|
|
|
80
|
-
describe("
|
|
81
|
+
describe("ERC20PeggedVariableCriteriaIncentive", () => {
|
|
81
82
|
beforeAll(async () => {
|
|
82
83
|
fixtures = await loadFixture(deployFixtures(defaultOptions));
|
|
83
84
|
});
|
|
@@ -86,7 +87,7 @@ describe("ERC20VariableCriteriaIncentive", () => {
|
|
|
86
87
|
budgets = await loadFixture(fundBudget(defaultOptions, fixtures));
|
|
87
88
|
erc20 = await loadFixture(fundErc20(defaultOptions));
|
|
88
89
|
erc721 = await loadFixture(fundErc721(defaultOptions));
|
|
89
|
-
|
|
90
|
+
erc20PeggedVariableCriteriaIncentive = fixtures.core.ERC20PeggedVariableCriteriaIncentive({
|
|
90
91
|
asset: budgets.erc20.assertValidAddress(),
|
|
91
92
|
reward: parseEther("1"),
|
|
92
93
|
limit: parseEther("10"),
|
|
@@ -97,48 +98,48 @@ describe("ERC20VariableCriteriaIncentive", () => {
|
|
|
97
98
|
|
|
98
99
|
boost = await freshBoost(fixtures, {
|
|
99
100
|
budget: budgets.budget,
|
|
100
|
-
incentives: [
|
|
101
|
+
incentives: [erc20PeggedVariableCriteriaIncentive],
|
|
101
102
|
});
|
|
102
103
|
expect(isAddress(boost.incentives[0]!.assertValidAddress())).toBe(true);
|
|
103
104
|
});
|
|
104
105
|
|
|
105
106
|
describe("Basic Parameters", () => {
|
|
106
107
|
test("should return correct asset address", async () => {
|
|
107
|
-
const asset = await
|
|
108
|
+
const asset = await erc20PeggedVariableCriteriaIncentive.asset();
|
|
108
109
|
expect(isAddressEqual(asset, budgets.erc20.assertValidAddress())).toBe(true);
|
|
109
110
|
});
|
|
110
111
|
|
|
111
112
|
test("should return correct peg token address", async () => {
|
|
112
|
-
const peg = await
|
|
113
|
+
const peg = await erc20PeggedVariableCriteriaIncentive.peg();
|
|
113
114
|
expect(isAddressEqual(peg, erc20.assertValidAddress())).toBe(true);
|
|
114
115
|
});
|
|
115
116
|
|
|
116
117
|
test("should return correct reward amount", async () => {
|
|
117
|
-
const reward = await
|
|
118
|
+
const reward = await erc20PeggedVariableCriteriaIncentive.reward();
|
|
118
119
|
expect(reward).toBe(parseEther("1"));
|
|
119
120
|
});
|
|
120
121
|
|
|
121
122
|
test("should return correct limit", async () => {
|
|
122
|
-
const limit = await
|
|
123
|
+
const limit = await erc20PeggedVariableCriteriaIncentive.limit();
|
|
123
124
|
expect(limit).toBe(parseEther("10"));
|
|
124
125
|
});
|
|
125
126
|
|
|
126
127
|
test("should return correct max reward", async () => {
|
|
127
|
-
const maxReward = await
|
|
128
|
+
const maxReward = await erc20PeggedVariableCriteriaIncentive.getMaxReward();
|
|
128
129
|
expect(maxReward).toBe(parseEther("20"));
|
|
129
130
|
});
|
|
130
131
|
});
|
|
131
132
|
|
|
132
133
|
describe("Remaining Claims", () => {
|
|
133
134
|
test("should calculate remaining claim potential correctly", async () => {
|
|
134
|
-
const remaining = await
|
|
135
|
-
const limit = await
|
|
136
|
-
const totalClaimed = await
|
|
135
|
+
const remaining = await erc20PeggedVariableCriteriaIncentive.getRemainingClaimPotential();
|
|
136
|
+
const limit = await erc20PeggedVariableCriteriaIncentive.limit();
|
|
137
|
+
const totalClaimed = await erc20PeggedVariableCriteriaIncentive.totalClaimed();
|
|
137
138
|
expect(remaining).toBe(limit - totalClaimed);
|
|
138
139
|
});
|
|
139
140
|
|
|
140
141
|
test("should return true for canBeClaimed when claims remain", async () => {
|
|
141
|
-
const canBeClaimed = await
|
|
142
|
+
const canBeClaimed = await erc20PeggedVariableCriteriaIncentive.canBeClaimed();
|
|
142
143
|
expect(canBeClaimed).toBe(true);
|
|
143
144
|
});
|
|
144
145
|
});
|
|
@@ -159,7 +160,7 @@ describe("ERC20VariableCriteriaIncentive", () => {
|
|
|
159
160
|
describe("Claim Data", () => {
|
|
160
161
|
test("should properly encode claim data", () => {
|
|
161
162
|
const rewardAmount = parseEther("1");
|
|
162
|
-
const encodedData =
|
|
163
|
+
const encodedData = erc20PeggedVariableCriteriaIncentive.buildClaimData(rewardAmount);
|
|
163
164
|
expect(encodedData).toBe(
|
|
164
165
|
"0x0000000000000000000000000000000000000000000000000de0b6b3a7640000"
|
|
165
166
|
);
|
|
@@ -168,14 +169,14 @@ describe("ERC20VariableCriteriaIncentive", () => {
|
|
|
168
169
|
|
|
169
170
|
describe("Owner", () => {
|
|
170
171
|
test("should return correct owner", async () => {
|
|
171
|
-
const owner = await
|
|
172
|
+
const owner = await erc20PeggedVariableCriteriaIncentive.owner();
|
|
172
173
|
expect(isAddressEqual(owner, fixtures.core.assertValidAddress())).toBe(true);
|
|
173
174
|
});
|
|
174
175
|
});
|
|
175
176
|
|
|
176
177
|
describe("Current Reward", () => {
|
|
177
178
|
test("should return valid current reward", async () => {
|
|
178
|
-
const currentReward = await
|
|
179
|
+
const currentReward = await erc20PeggedVariableCriteriaIncentive.currentReward();
|
|
179
180
|
expect(currentReward).toBeDefined();
|
|
180
181
|
expect(currentReward).toBeTypeOf("bigint");
|
|
181
182
|
});
|
|
@@ -190,7 +191,7 @@ describe("ERC20VariableCriteriaIncentive", () => {
|
|
|
190
191
|
recipient,
|
|
191
192
|
1n,
|
|
192
193
|
);
|
|
193
|
-
const scalar = await
|
|
194
|
+
const scalar = await erc20PeggedVariableCriteriaIncentive.getIncentiveScalar({
|
|
194
195
|
hash,
|
|
195
196
|
chainId: 31337,
|
|
196
197
|
knownSignatures: allKnownSignatures,
|
|
@@ -202,7 +203,7 @@ describe("ERC20VariableCriteriaIncentive", () => {
|
|
|
202
203
|
test("should return a valid scalar for event-based criteria", async () => {
|
|
203
204
|
boost = await freshBoost(fixtures, {
|
|
204
205
|
budget: budgets.budget,
|
|
205
|
-
incentives: [
|
|
206
|
+
incentives: [erc20PeggedVariableCriteriaIncentive],
|
|
206
207
|
});
|
|
207
208
|
const recipient = accounts[1].account;
|
|
208
209
|
const { hash } = await erc721.transferFromRaw(
|
|
@@ -210,7 +211,7 @@ describe("ERC20VariableCriteriaIncentive", () => {
|
|
|
210
211
|
recipient,
|
|
211
212
|
1n,
|
|
212
213
|
);
|
|
213
|
-
const scalar = await
|
|
214
|
+
const scalar = await erc20PeggedVariableCriteriaIncentive.getIncentiveScalar({
|
|
214
215
|
hash,
|
|
215
216
|
chainId: 31337,
|
|
216
217
|
knownSignatures: allKnownSignatures,
|
|
@@ -223,7 +224,7 @@ describe("ERC20VariableCriteriaIncentive", () => {
|
|
|
223
224
|
// Ensure that the gasRebateIncentiveCriteria returns the correct structure
|
|
224
225
|
const gasRebateCriteria = gasRebateIncentiveCriteria();
|
|
225
226
|
|
|
226
|
-
|
|
227
|
+
erc20PeggedVariableCriteriaIncentive = fixtures.core.ERC20PeggedVariableCriteriaIncentive({
|
|
227
228
|
asset: budgets.erc20.assertValidAddress(),
|
|
228
229
|
reward: 1n,
|
|
229
230
|
limit: 1n,
|
|
@@ -242,7 +243,7 @@ describe("ERC20VariableCriteriaIncentive", () => {
|
|
|
242
243
|
|
|
243
244
|
boost = await freshBoost(fixtures, {
|
|
244
245
|
budget: budgets.budget,
|
|
245
|
-
incentives: [
|
|
246
|
+
incentives: [erc20PeggedVariableCriteriaIncentive],
|
|
246
247
|
});
|
|
247
248
|
|
|
248
249
|
// Validate that the deployed incentive has the correct criteria set up
|
|
@@ -261,7 +262,7 @@ describe("ERC20VariableCriteriaIncentive", () => {
|
|
|
261
262
|
const { hash } = await erc20.mintRaw(recipient, parseEther("100"));
|
|
262
263
|
|
|
263
264
|
try {
|
|
264
|
-
await
|
|
265
|
+
await erc20PeggedVariableCriteriaIncentive.getIncentiveScalar({
|
|
265
266
|
hash,
|
|
266
267
|
chainId: 31337,
|
|
267
268
|
knownSignatures: allKnownSignatures,
|
|
@@ -276,7 +277,7 @@ describe("ERC20VariableCriteriaIncentive", () => {
|
|
|
276
277
|
const { hash } = await erc20.mintRaw(recipient, parseEther("100"));
|
|
277
278
|
|
|
278
279
|
try {
|
|
279
|
-
await
|
|
280
|
+
await erc20PeggedVariableCriteriaIncentive.getIncentiveScalar({
|
|
280
281
|
hash,
|
|
281
282
|
chainId: 31337,
|
|
282
283
|
knownSignatures: allKnownSignatures,
|
|
@@ -299,11 +300,11 @@ describe("ERC20VariableCriteriaIncentive", () => {
|
|
|
299
300
|
// rebase this
|
|
300
301
|
const boost = await freshBoost(fixtures, {
|
|
301
302
|
budget: budgets.budget,
|
|
302
|
-
incentives: [
|
|
303
|
+
incentives: [erc20PeggedVariableCriteriaIncentive],
|
|
303
304
|
});
|
|
304
305
|
const [amount, address] = await budgets.budget.clawbackFromTarget(
|
|
305
306
|
fixtures.core.assertValidAddress(),
|
|
306
|
-
|
|
307
|
+
erc20PeggedVariableCriteriaIncentive.buildClawbackData(1n),
|
|
307
308
|
boost.id,
|
|
308
309
|
0,
|
|
309
310
|
);
|
|
@@ -312,4 +313,45 @@ describe("ERC20VariableCriteriaIncentive", () => {
|
|
|
312
313
|
true,
|
|
313
314
|
);
|
|
314
315
|
});
|
|
316
|
+
|
|
317
|
+
test('can claim', async () => {
|
|
318
|
+
const referrer = accounts.at(1)?.account!
|
|
319
|
+
const trustedSigner = accounts.at(0)!
|
|
320
|
+
const claimant = trustedSigner.account!;
|
|
321
|
+
const asset = await erc20PeggedVariableCriteriaIncentive.asset()
|
|
322
|
+
const signedAmount = 2n
|
|
323
|
+
const incentiveData = erc20PeggedVariableCriteriaIncentive.buildClaimData(signedAmount)
|
|
324
|
+
const claimDataPayload = await boost.validator.encodeClaimData({
|
|
325
|
+
signer: trustedSigner,
|
|
326
|
+
incentiveData,
|
|
327
|
+
chainId: defaultOptions.config.chains[0].id,
|
|
328
|
+
incentiveQuantity: boost.incentives.length,
|
|
329
|
+
claimant,
|
|
330
|
+
boostId: boost.id
|
|
331
|
+
})
|
|
332
|
+
|
|
333
|
+
// verify reward amount
|
|
334
|
+
const incentiveClaimAmount = await erc20PeggedVariableCriteriaIncentive.decodeClaimData(claimDataPayload)
|
|
335
|
+
expect(incentiveClaimAmount).toBe(signedAmount)
|
|
336
|
+
|
|
337
|
+
const balanceBeforeClaim = await readMockErc20BalanceOf(defaultOptions.config, {
|
|
338
|
+
address: asset,
|
|
339
|
+
args: [claimant],
|
|
340
|
+
})
|
|
341
|
+
|
|
342
|
+
await fixtures.core.claimIncentiveFor(
|
|
343
|
+
boost.id,
|
|
344
|
+
0n,
|
|
345
|
+
referrer,
|
|
346
|
+
claimDataPayload,
|
|
347
|
+
claimant,
|
|
348
|
+
);
|
|
349
|
+
|
|
350
|
+
const balanceAfterClaim = await readMockErc20BalanceOf(defaultOptions.config, {
|
|
351
|
+
address: asset,
|
|
352
|
+
args: [claimant],
|
|
353
|
+
})
|
|
354
|
+
|
|
355
|
+
expect(balanceAfterClaim - balanceBeforeClaim).toBe(signedAmount);
|
|
356
|
+
})
|
|
315
357
|
});
|
|
@@ -29,6 +29,7 @@ import {
|
|
|
29
29
|
decodeAbiParameters,
|
|
30
30
|
decodeFunctionData,
|
|
31
31
|
encodeAbiParameters,
|
|
32
|
+
parseEther,
|
|
32
33
|
parseEventLogs,
|
|
33
34
|
zeroAddress,
|
|
34
35
|
} from 'viem';
|
|
@@ -651,27 +652,108 @@ export class ERC20PeggedVariableCriteriaIncentive extends DeployableTarget<
|
|
|
651
652
|
* Builds the claim data for the ERC20PeggedVariableCriteriaIncentivePayload.
|
|
652
653
|
*
|
|
653
654
|
* @public
|
|
654
|
-
* @param {bigint}
|
|
655
|
+
* @param {bigint} signedAmount
|
|
655
656
|
* @returns {Hash} Returns the encoded claim data
|
|
656
657
|
* @description This function returns the encoded claim data for the ERC20PeggedVariableCriteriaIncentivePayload.
|
|
657
658
|
*/
|
|
658
|
-
public buildClaimData(
|
|
659
|
+
public buildClaimData(signedAmount: bigint) {
|
|
659
660
|
return encodeAbiParameters(
|
|
660
|
-
[{ type: 'uint256', name: '
|
|
661
|
-
[
|
|
661
|
+
[{ type: 'uint256', name: 'signedAmount' }],
|
|
662
|
+
[signedAmount],
|
|
662
663
|
);
|
|
663
664
|
}
|
|
664
665
|
|
|
665
666
|
/**
|
|
666
|
-
* Decodes claim data for the ERC20PeggedVariableCriteriaIncentive, returning the
|
|
667
|
+
* Decodes claim data for the ERC20PeggedVariableCriteriaIncentive, returning the claim amount.
|
|
667
668
|
* Useful when deriving amount claimed from logs.
|
|
668
669
|
*
|
|
669
670
|
* @public
|
|
670
671
|
* @param {Hex} claimData
|
|
671
672
|
* @returns {BigInt} Returns the reward amount from a claim data payload
|
|
672
673
|
*/
|
|
673
|
-
public decodeClaimData(
|
|
674
|
-
|
|
674
|
+
public async decodeClaimData(claimData: Hex) {
|
|
675
|
+
const boostClaimData = decodeAbiParameters(
|
|
676
|
+
[
|
|
677
|
+
{
|
|
678
|
+
type: 'tuple',
|
|
679
|
+
name: 'BoostClaimData',
|
|
680
|
+
components: [
|
|
681
|
+
{ type: 'bytes', name: 'validatorData' },
|
|
682
|
+
{ type: 'bytes', name: 'incentiveData' },
|
|
683
|
+
],
|
|
684
|
+
},
|
|
685
|
+
],
|
|
686
|
+
claimData,
|
|
687
|
+
);
|
|
688
|
+
const signedAmount = decodeAbiParameters(
|
|
689
|
+
[{ type: 'uint256' }],
|
|
690
|
+
boostClaimData[0].incentiveData,
|
|
691
|
+
)[0];
|
|
692
|
+
let claimAmount = signedAmount;
|
|
693
|
+
const [reward, maxReward] = await Promise.all([
|
|
694
|
+
this.reward(),
|
|
695
|
+
this.getMaxReward(),
|
|
696
|
+
]);
|
|
697
|
+
|
|
698
|
+
if (reward === 0n) {
|
|
699
|
+
return claimAmount;
|
|
700
|
+
} else {
|
|
701
|
+
claimAmount = (reward * signedAmount) / parseEther('1');
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
if (maxReward !== 0n && claimAmount > maxReward) {
|
|
705
|
+
claimAmount = maxReward;
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
return claimAmount;
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
/**
|
|
712
|
+
* Decodes claim data for the ERC20PeggedVariableCriteriaIncentive, returning the claim amount.
|
|
713
|
+
* Useful when deriving amount claimed from logs.
|
|
714
|
+
* Use this function instead of `decodeClaimData` if you have reward details.
|
|
715
|
+
*
|
|
716
|
+
* @public
|
|
717
|
+
* @param {Hex} claimData
|
|
718
|
+
* @param {bigint} [reward]
|
|
719
|
+
* @param {bigint} [maxReward]
|
|
720
|
+
* @returns {BigInt} Returns the reward amount from a claim data payload
|
|
721
|
+
*/
|
|
722
|
+
public decodeClaimDataWithRewardDetails(
|
|
723
|
+
claimData: Hex,
|
|
724
|
+
reward: bigint,
|
|
725
|
+
maxReward: bigint,
|
|
726
|
+
) {
|
|
727
|
+
const boostClaimData = decodeAbiParameters(
|
|
728
|
+
[
|
|
729
|
+
{
|
|
730
|
+
type: 'tuple',
|
|
731
|
+
name: 'BoostClaimData',
|
|
732
|
+
components: [
|
|
733
|
+
{ type: 'bytes', name: 'validatorData' },
|
|
734
|
+
{ type: 'bytes', name: 'incentiveData' },
|
|
735
|
+
],
|
|
736
|
+
},
|
|
737
|
+
],
|
|
738
|
+
claimData,
|
|
739
|
+
);
|
|
740
|
+
const signedAmount = decodeAbiParameters(
|
|
741
|
+
[{ type: 'uint256' }],
|
|
742
|
+
boostClaimData[0].incentiveData,
|
|
743
|
+
)[0];
|
|
744
|
+
let claimAmount = signedAmount;
|
|
745
|
+
|
|
746
|
+
if (reward === 0n) {
|
|
747
|
+
return claimAmount;
|
|
748
|
+
} else {
|
|
749
|
+
claimAmount = (reward * signedAmount) / parseEther('1');
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
if (maxReward !== 0n && claimAmount > maxReward) {
|
|
753
|
+
claimAmount = maxReward;
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
return claimAmount;
|
|
675
757
|
}
|
|
676
758
|
}
|
|
677
759
|
|
package/src/errors.ts
CHANGED
|
@@ -865,3 +865,53 @@ export class BoostNotFoundError extends Error {
|
|
|
865
865
|
this.id = id;
|
|
866
866
|
}
|
|
867
867
|
}
|
|
868
|
+
|
|
869
|
+
/**
|
|
870
|
+
* Thrown when a tuple decoding operation encounters unexpected structure or data.
|
|
871
|
+
*
|
|
872
|
+
* @export
|
|
873
|
+
* @class InvalidTupleDecodingError
|
|
874
|
+
* @typedef {InvalidTupleDecodingError}
|
|
875
|
+
* @extends {Error}
|
|
876
|
+
*/
|
|
877
|
+
export class InvalidTupleDecodingError extends Error {
|
|
878
|
+
/**
|
|
879
|
+
* The index of the tuple that caused the error.
|
|
880
|
+
*
|
|
881
|
+
* @type {number}
|
|
882
|
+
*/
|
|
883
|
+
public readonly tupleIndex: number;
|
|
884
|
+
|
|
885
|
+
/**
|
|
886
|
+
* Creates an instance of InvalidTupleDecodingError.
|
|
887
|
+
*
|
|
888
|
+
* @constructor
|
|
889
|
+
* @param {string} tupleName - The name or identifier of the tuple that failed to decode.
|
|
890
|
+
* @param {string} [message] - Optional custom message for more context.
|
|
891
|
+
*/
|
|
892
|
+
constructor(message?: string, tupleIndex?: number) {
|
|
893
|
+
super(message || `Failed to decode tuple: ${tupleIndex}`);
|
|
894
|
+
this.tupleIndex = tupleIndex ?? -1;
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
export class InvalidTupleEncodingError extends Error {
|
|
899
|
+
/**
|
|
900
|
+
* The index of the tuple that caused the error.
|
|
901
|
+
*
|
|
902
|
+
* @type {number}
|
|
903
|
+
*/
|
|
904
|
+
public readonly tupleIndex: number;
|
|
905
|
+
|
|
906
|
+
/**
|
|
907
|
+
* Creates an instance of InvalidTupleEncodingError.
|
|
908
|
+
*
|
|
909
|
+
* @constructor
|
|
910
|
+
* @param {string} tupleName - The name or identifier of the tuple that failed to encode.
|
|
911
|
+
* @param {string} [message] - Optional custom message for more context.
|
|
912
|
+
*/
|
|
913
|
+
constructor(message?: string, tupleIndex?: number) {
|
|
914
|
+
super(message || `Failed to encode tuple: ${tupleIndex}`);
|
|
915
|
+
this.tupleIndex = tupleIndex ?? -1;
|
|
916
|
+
}
|
|
917
|
+
}
|