@boostxyz/sdk 5.3.1 → 6.0.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.
Files changed (143) hide show
  1. package/dist/Actions/Action.cjs +1 -1
  2. package/dist/Actions/Action.js +3 -3
  3. package/dist/Actions/EventAction.cjs +1 -1
  4. package/dist/Actions/EventAction.cjs.map +1 -1
  5. package/dist/Actions/EventAction.d.ts +13 -3
  6. package/dist/Actions/EventAction.d.ts.map +1 -1
  7. package/dist/Actions/EventAction.js +216 -193
  8. package/dist/Actions/EventAction.js.map +1 -1
  9. package/dist/AllowLists/AllowList.cjs +1 -1
  10. package/dist/AllowLists/AllowList.js +2 -2
  11. package/dist/AllowLists/SimpleAllowList.cjs +1 -1
  12. package/dist/AllowLists/SimpleAllowList.cjs.map +1 -1
  13. package/dist/AllowLists/SimpleAllowList.js +24 -24
  14. package/dist/AllowLists/SimpleAllowList.js.map +1 -1
  15. package/dist/AllowLists/SimpleDenyList.cjs +1 -1
  16. package/dist/AllowLists/SimpleDenyList.js +3 -3
  17. package/dist/Auth/PassthroughAuth.cjs +1 -1
  18. package/dist/Auth/PassthroughAuth.cjs.map +1 -1
  19. package/dist/Auth/PassthroughAuth.js +14 -14
  20. package/dist/Boost.cjs.map +1 -1
  21. package/dist/Boost.js.map +1 -1
  22. package/dist/BoostCore.cjs +2 -2
  23. package/dist/BoostCore.cjs.map +1 -1
  24. package/dist/BoostCore.d.ts +247 -0
  25. package/dist/BoostCore.d.ts.map +1 -1
  26. package/dist/BoostCore.js +421 -259
  27. package/dist/BoostCore.js.map +1 -1
  28. package/dist/BoostRegistry.cjs +1 -1
  29. package/dist/BoostRegistry.cjs.map +1 -1
  30. package/dist/BoostRegistry.js +43 -43
  31. package/dist/BoostRegistry.js.map +1 -1
  32. package/dist/Budget-DMbfdTom.cjs +2 -0
  33. package/dist/{Budget-AoNx7uFd.cjs.map → Budget-DMbfdTom.cjs.map} +1 -1
  34. package/dist/Budget-DO6sGTIR.js +463 -0
  35. package/dist/{Budget-DYIV9iNK.js.map → Budget-DO6sGTIR.js.map} +1 -1
  36. package/dist/Budgets/Budget.cjs +1 -1
  37. package/dist/Budgets/Budget.js +2 -2
  38. package/dist/Budgets/ManagedBudget.cjs +1 -1
  39. package/dist/Budgets/ManagedBudget.cjs.map +1 -1
  40. package/dist/Budgets/ManagedBudget.js +53 -53
  41. package/dist/Budgets/ManagedBudgetWithFees.d.ts +26 -0
  42. package/dist/Budgets/ManagedBudgetWithFees.d.ts.map +1 -1
  43. package/dist/Deployable/DeployableTarget.cjs +1 -1
  44. package/dist/Deployable/DeployableTarget.js +1 -1
  45. package/dist/Deployable/DeployableTargetWithRBAC.cjs +1 -1
  46. package/dist/Deployable/DeployableTargetWithRBAC.js +15 -15
  47. package/dist/Incentive-Boviez4z.js +1036 -0
  48. package/dist/Incentive-Boviez4z.js.map +1 -0
  49. package/dist/Incentive-wM4zizTH.cjs +2 -0
  50. package/dist/Incentive-wM4zizTH.cjs.map +1 -0
  51. package/dist/Incentives/AllowListIncentive.cjs +1 -1
  52. package/dist/Incentives/AllowListIncentive.cjs.map +1 -1
  53. package/dist/Incentives/AllowListIncentive.d.ts +9 -1
  54. package/dist/Incentives/AllowListIncentive.d.ts.map +1 -1
  55. package/dist/Incentives/AllowListIncentive.js +64 -47
  56. package/dist/Incentives/AllowListIncentive.js.map +1 -1
  57. package/dist/Incentives/CGDAIncentive.cjs +1 -1
  58. package/dist/Incentives/CGDAIncentive.cjs.map +1 -1
  59. package/dist/Incentives/CGDAIncentive.d.ts +12 -1
  60. package/dist/Incentives/CGDAIncentive.d.ts.map +1 -1
  61. package/dist/Incentives/CGDAIncentive.js +64 -33
  62. package/dist/Incentives/CGDAIncentive.js.map +1 -1
  63. package/dist/Incentives/ERC20Incentive.cjs +1 -1
  64. package/dist/Incentives/ERC20Incentive.cjs.map +1 -1
  65. package/dist/Incentives/ERC20Incentive.d.ts +37 -1
  66. package/dist/Incentives/ERC20Incentive.d.ts.map +1 -1
  67. package/dist/Incentives/ERC20Incentive.js +73 -49
  68. package/dist/Incentives/ERC20Incentive.js.map +1 -1
  69. package/dist/Incentives/ERC20PeggedIncentive.d.ts +35 -0
  70. package/dist/Incentives/ERC20PeggedIncentive.d.ts.map +1 -1
  71. package/dist/Incentives/ERC20PeggedVariableCriteriaIncentive.d.ts +35 -0
  72. package/dist/Incentives/ERC20PeggedVariableCriteriaIncentive.d.ts.map +1 -1
  73. package/dist/Incentives/ERC20VariableCriteriaIncentive.cjs +1 -1
  74. package/dist/Incentives/ERC20VariableCriteriaIncentive.cjs.map +1 -1
  75. package/dist/Incentives/ERC20VariableCriteriaIncentive.d.ts +25 -0
  76. package/dist/Incentives/ERC20VariableCriteriaIncentive.d.ts.map +1 -1
  77. package/dist/Incentives/ERC20VariableCriteriaIncentive.js +24 -24
  78. package/dist/Incentives/ERC20VariableCriteriaIncentive.js.map +1 -1
  79. package/dist/Incentives/ERC20VariableIncentive.cjs +1 -1
  80. package/dist/Incentives/ERC20VariableIncentive.cjs.map +1 -1
  81. package/dist/Incentives/ERC20VariableIncentive.d.ts +35 -0
  82. package/dist/Incentives/ERC20VariableIncentive.d.ts.map +1 -1
  83. package/dist/Incentives/ERC20VariableIncentive.js +55 -38
  84. package/dist/Incentives/ERC20VariableIncentive.js.map +1 -1
  85. package/dist/Incentives/Incentive.cjs +1 -1
  86. package/dist/Incentives/Incentive.d.ts +25 -0
  87. package/dist/Incentives/Incentive.d.ts.map +1 -1
  88. package/dist/Incentives/Incentive.js +2 -2
  89. package/dist/Incentives/PointsIncentive.cjs +1 -1
  90. package/dist/Incentives/PointsIncentive.cjs.map +1 -1
  91. package/dist/Incentives/PointsIncentive.d.ts +8 -0
  92. package/dist/Incentives/PointsIncentive.d.ts.map +1 -1
  93. package/dist/Incentives/PointsIncentive.js +50 -28
  94. package/dist/Incentives/PointsIncentive.js.map +1 -1
  95. package/dist/SimpleDenyList-CW4RwwRw.js +133 -0
  96. package/dist/{SimpleDenyList-ByAr4X1r.js.map → SimpleDenyList-CW4RwwRw.js.map} +1 -1
  97. package/dist/SimpleDenyList-Cybtz7AK.cjs +2 -0
  98. package/dist/{SimpleDenyList-CsRXJPwm.cjs.map → SimpleDenyList-Cybtz7AK.cjs.map} +1 -1
  99. package/dist/Validators/LimitedSignerValidator.cjs +1 -1
  100. package/dist/Validators/LimitedSignerValidator.cjs.map +1 -1
  101. package/dist/Validators/LimitedSignerValidator.js +33 -33
  102. package/dist/Validators/LimitedSignerValidator.js.map +1 -1
  103. package/dist/Validators/SignerValidator.cjs +1 -1
  104. package/dist/Validators/SignerValidator.cjs.map +1 -1
  105. package/dist/Validators/SignerValidator.js +26 -26
  106. package/dist/Validators/SignerValidator.js.map +1 -1
  107. package/dist/Validators/Validator.cjs +1 -1
  108. package/dist/Validators/Validator.js +3 -3
  109. package/dist/deployments-CIXw_WKk.cjs +2 -0
  110. package/dist/deployments-CIXw_WKk.cjs.map +1 -0
  111. package/dist/{deployments-D0fs26TV.js → deployments-DqjtOTUr.js} +47 -47
  112. package/dist/{deployments-D0fs26TV.js.map → deployments-DqjtOTUr.js.map} +1 -1
  113. package/dist/deployments.json +24 -24
  114. package/dist/generated-DgXPUgXl.cjs +3 -0
  115. package/dist/generated-DgXPUgXl.cjs.map +1 -0
  116. package/dist/{generated-Cyvr_Tjx.js → generated-pJZHmRCK.js} +728 -459
  117. package/dist/generated-pJZHmRCK.js.map +1 -0
  118. package/dist/index.cjs +1 -1
  119. package/dist/index.js +137 -136
  120. package/package.json +1 -1
  121. package/src/Actions/EventAction.ts +108 -27
  122. package/src/BoostCore.test.ts +124 -2
  123. package/src/BoostCore.ts +227 -0
  124. package/src/Incentives/AllowListIncentive.ts +17 -0
  125. package/src/Incentives/CGDAIncentive.ts +31 -0
  126. package/src/Incentives/ERC20Incentive.ts +27 -0
  127. package/src/Incentives/ERC20PeggedIncentive.ts +18 -0
  128. package/src/Incentives/ERC20PeggedVariableCriteriaIncentive.ts +26 -0
  129. package/src/Incentives/ERC20VariableIncentive.ts +18 -0
  130. package/src/Incentives/PointsIncentive.ts +22 -0
  131. package/dist/Budget-AoNx7uFd.cjs +0 -2
  132. package/dist/Budget-DYIV9iNK.js +0 -463
  133. package/dist/Incentive-BbkfwGOb.cjs +0 -2
  134. package/dist/Incentive-BbkfwGOb.cjs.map +0 -1
  135. package/dist/Incentive-qlnv5kQB.js +0 -991
  136. package/dist/Incentive-qlnv5kQB.js.map +0 -1
  137. package/dist/SimpleDenyList-ByAr4X1r.js +0 -133
  138. package/dist/SimpleDenyList-CsRXJPwm.cjs +0 -2
  139. package/dist/deployments-DoIOqxco.cjs +0 -2
  140. package/dist/deployments-DoIOqxco.cjs.map +0 -1
  141. package/dist/generated-Cyvr_Tjx.js.map +0 -1
  142. package/dist/generated-DtYPHhtX.cjs +0 -3
  143. package/dist/generated-DtYPHhtX.cjs.map +0 -1
@@ -765,8 +765,8 @@ describe("BoostCore", () => {
765
765
  await new Promise((resolve) => {
766
766
  setTimeout(resolve, 500);
767
767
  });
768
-
769
- expect(subscription).toHaveBeenCalledTimes(1);
768
+ // This should be called once for each event
769
+ expect(subscription).toHaveBeenCalledTimes(2);
770
770
  });
771
771
 
772
772
  test("can set a passthrough auth scheme", async () => {
@@ -953,3 +953,125 @@ describe("BoostCore", () => {
953
953
  expect(feesInfo.asset.toLowerCase()).toBe(erc20.assertValidAddress());
954
954
  });
955
955
  });
956
+
957
+ describe("Top-Up Incentives", () => {
958
+ let incentive: ReturnType<typeof fixtures.core.ERC20Incentive>;
959
+ let boostId: bigint;
960
+
961
+ beforeAll(async () => {
962
+ const { core } = fixtures;
963
+ const { budget, erc20 } = budgets;
964
+
965
+ incentive = core.ERC20Incentive({
966
+ asset: erc20.assertValidAddress(),
967
+ strategy: StrategyType.POOL,
968
+ reward: parseEther("1"),
969
+ limit: 5n,
970
+ manager: budget.assertValidAddress(),
971
+ });
972
+ await erc20.mint(defaultOptions.account.address, parseEther("110"));
973
+ await erc20.approve(budget.assertValidAddress(), parseEther("110"));
974
+ await budget.allocate({
975
+ amount: parseEther("110"),
976
+ asset: erc20.assertValidAddress(),
977
+ target: defaultOptions.account.address,
978
+ });
979
+
980
+
981
+ const createdBoost = await core.createBoost({
982
+ protocolFee: 0n,
983
+ maxParticipants: 5n,
984
+ budget,
985
+ action: core.EventAction(
986
+ makeMockEventActionPayload(
987
+ core.assertValidAddress(),
988
+ erc20.assertValidAddress(),
989
+ ),
990
+ ),
991
+ validator: core.SignerValidator({
992
+ signers: [defaultOptions.account.address],
993
+ validatorCaller: defaultOptions.account.address,
994
+ }),
995
+ allowList: core.SimpleAllowList({
996
+ owner: defaultOptions.account.address,
997
+ allowed: [defaultOptions.account.address],
998
+ }),
999
+ incentives: [incentive],
1000
+ });
1001
+ boostId = createdBoost.id;
1002
+ });
1003
+
1004
+ test("can top up from a budget (pre-fee)", async () => {
1005
+ const { core } = fixtures;
1006
+ const { budget, erc20 } = budgets;
1007
+
1008
+ console.log("budget", budget.assertValidAddress());
1009
+
1010
+ const netTopup = parseEther("5");
1011
+
1012
+ await core.topupIncentiveFromBudgetPreFee(boostId, 0n, netTopup, budget.assertValidAddress());
1013
+ console.log("topup done");
1014
+
1015
+ expect(await incentive.limit()).toBe(5n + 5n); // original limit 5 + topup 5
1016
+ });
1017
+
1018
+ test("can top up from a budget (post-fee)", async () => {
1019
+ const { core } = fixtures;
1020
+ const { budget } = budgets;
1021
+
1022
+ const total = parseEther("5.5");
1023
+ await core.topupIncentiveFromBudgetPostFee(boostId, 0n, total, budget.assertValidAddress());
1024
+
1025
+ expect(await incentive.limit()).toBe(10n + 5n);
1026
+ });
1027
+
1028
+ test("pre-fee and post-fee top-ups lead to the same net top-up", async () => {
1029
+ const { core } = fixtures;
1030
+ const { budget } = budgets;
1031
+
1032
+ const net = parseEther("2");
1033
+ const netPlusFee = parseEther("2.2");
1034
+
1035
+ await core.topupIncentiveFromBudgetPreFee(boostId, 0n, net, budget.assertValidAddress());
1036
+
1037
+ await core.topupIncentiveFromBudgetPostFee(boostId, 0n, netPlusFee, budget.assertValidAddress());
1038
+
1039
+ expect(await incentive.limit()).toBe(15n + 4n);
1040
+ });
1041
+
1042
+ test("can top up from sender (pre-fee)", async () => {
1043
+ const { core } = fixtures;
1044
+ const { erc20 } = budgets;
1045
+
1046
+ const netTopup = parseEther("10");
1047
+ const netPlusFee = parseEther("11");
1048
+ await erc20.mint(defaultOptions.account.address, netPlusFee);
1049
+ await erc20.approve(core.assertValidAddress(), netPlusFee);
1050
+
1051
+ await core.topupIncentiveFromSenderPreFee(boostId, 0n, netTopup);
1052
+
1053
+ expect(await incentive.limit()).toBe(19n + 10n);
1054
+ });
1055
+
1056
+ test("can top up from sender (post-fee)", async () => {
1057
+ const { core } = fixtures;
1058
+ const { erc20 } = budgets;
1059
+
1060
+ const totalWithFee = parseEther("5.5");
1061
+ await erc20.mint(defaultOptions.account.address, totalWithFee);
1062
+ await erc20.approve(core.assertValidAddress(), totalWithFee);
1063
+
1064
+ await core.topupIncentiveFromSenderPostFee(boostId, 0n, totalWithFee);
1065
+
1066
+ expect(await incentive.limit()).toBe(29n + 5n);
1067
+ });
1068
+
1069
+ test("throws if net top-up is zero", async () => {
1070
+ const { core } = fixtures;
1071
+ const { budget } = budgets;
1072
+
1073
+ await expect(async () => {
1074
+ await core.topupIncentiveFromBudgetPreFee(boostId, 0n, 0n, budget.assertValidAddress());
1075
+ }).rejects.toThrowError();
1076
+ });
1077
+ });
package/src/BoostCore.ts CHANGED
@@ -12,11 +12,15 @@ import {
12
12
  simulateBoostCoreCreateBoost,
13
13
  simulateBoostCoreSetCreateBoostAuth,
14
14
  simulateBoostCoreSetProtocolFeeReceiver,
15
+ simulateBoostCoreTopupIncentiveFromBudget,
16
+ simulateBoostCoreTopupIncentiveFromSender,
15
17
  writeBoostCoreClaimIncentive,
16
18
  writeBoostCoreClaimIncentiveFor,
17
19
  writeBoostCoreCreateBoost,
18
20
  writeBoostCoreSetCreateBoostAuth,
19
21
  writeBoostCoreSetProtocolFeeReceiver,
22
+ writeBoostCoreTopupIncentiveFromBudget,
23
+ writeBoostCoreTopupIncentiveFromSender,
20
24
  } from '@boostxyz/evm';
21
25
  import { bytecode } from '@boostxyz/evm/artifacts/contracts/BoostCore.sol/BoostCore.json';
22
26
  import {
@@ -24,6 +28,7 @@ import {
24
28
  getAccount,
25
29
  getChains,
26
30
  getTransactionReceipt,
31
+ readContract,
27
32
  waitForTransactionReceipt,
28
33
  } from '@wagmi/core';
29
34
  import { createWriteContract } from '@wagmi/core/codegen';
@@ -31,6 +36,8 @@ import {
31
36
  type Address,
32
37
  type ContractEventName,
33
38
  type Hex,
39
+ decodeAbiParameters,
40
+ encodeAbiParameters,
34
41
  encodePacked,
35
42
  keccak256,
36
43
  parseEventLogs,
@@ -1654,4 +1661,224 @@ export class BoostCore extends Deployable<
1654
1661
  ...this.optionallyAttachAccount(options.account),
1655
1662
  };
1656
1663
  }
1664
+ /**
1665
+ * Prepares and executes a top-up from a Budget, specifying the net top-up amount
1666
+ * that should land in the incentive (the protocol fee is added automatically).
1667
+ *
1668
+ * @public
1669
+ * @async
1670
+ * @param {bigint} boostId The ID of the Boost
1671
+ * @param {bigint} incentiveId The ID of the incentive within that Boost
1672
+ * @param {any} topupPayload A typed struct for the incentive’s top-up parameters
1673
+ * @param {Address} [budget] Optional override budget address (otherwise uses the Boost’s budget)
1674
+ * @param {?WriteParams} [params] Additional transaction overrides
1675
+ * @returns {Promise<{ hash: Hex; result: void }>} The transaction hash and simulation result
1676
+ */
1677
+ public async topupIncentiveFromBudgetPreFee(
1678
+ boostId: bigint,
1679
+ incentiveId: bigint,
1680
+ topupAmount: bigint,
1681
+ budget?: Address,
1682
+ params?: WriteParams,
1683
+ ) {
1684
+ const boost = await this.getBoost(boostId, params);
1685
+ if (incentiveId >= boost.incentives.length) {
1686
+ throw new Error(`Incentive ID ${incentiveId} out of range`);
1687
+ }
1688
+ const incentive = boost.incentives[Number(incentiveId)];
1689
+ if (!incentive) {
1690
+ throw new Error(`Incentive with ID ${incentiveId} not found`);
1691
+ }
1692
+
1693
+ const incentiveData = await incentive.getTopupPayload(topupAmount);
1694
+
1695
+ if (!(topupAmount > 0)) {
1696
+ throw new Error('Top-up amount must be greater than zero');
1697
+ }
1698
+
1699
+ return await this.topupIncentiveFromBudgetRaw(
1700
+ boostId,
1701
+ incentiveId,
1702
+ incentiveData,
1703
+ budget,
1704
+ params,
1705
+ );
1706
+ }
1707
+
1708
+ /**
1709
+ * Prepares and executes a top-up from a Budget, specifying the entire total tokens
1710
+ * (incentive + fee) you want to disburse. We'll back-calculate how many tokens land
1711
+ * in the incentive.
1712
+ *
1713
+ * @public
1714
+ * @async
1715
+ * @param {bigint} boostId The ID of the Boost
1716
+ * @param {bigint} incentiveId The ID of the incentive within that Boost
1717
+ * @param {any} topupPayload A typed struct for the incentive’s top-up parameters
1718
+ * @param {bigint} totalAmount The total tokens to disburse
1719
+ * @param {Address} [budget] Optional override budget address
1720
+ * @param {?WriteParams} [params] Additional transaction overrides
1721
+ * @returns {Promise<{ hash: Hex; result: void }>}
1722
+ */
1723
+ public async topupIncentiveFromBudgetPostFee(
1724
+ boostId: bigint,
1725
+ incentiveId: bigint,
1726
+ totalAmount: bigint,
1727
+ budget?: Address,
1728
+ params?: WriteParams,
1729
+ ) {
1730
+ const feeBps = await this.protocolFee(params);
1731
+ const topupAmount =
1732
+ (totalAmount * FEE_DENOMINATOR) / (FEE_DENOMINATOR + feeBps);
1733
+ return this.topupIncentiveFromBudgetPreFee(
1734
+ boostId,
1735
+ incentiveId,
1736
+ topupAmount,
1737
+ budget,
1738
+ params,
1739
+ );
1740
+ }
1741
+
1742
+ /**
1743
+ * Prepares and executes a top-up from the caller (msg.sender), specifying the net top-up
1744
+ * to land in the incentive. We'll add the protocol fee on top automatically.
1745
+ *
1746
+ * @public
1747
+ * @async
1748
+ * @param {bigint} boostId The ID of the Boost
1749
+ * @param {bigint} incentiveId The ID of the incentive within that Boost
1750
+ * @param {any} topupPayload A typed struct for the incentive’s top-up parameters
1751
+ * @param {?WriteParams} [params]
1752
+ * @returns {Promise<{ hash: Hex; result: void }>}
1753
+ */
1754
+ public async topupIncentiveFromSenderPreFee(
1755
+ boostId: bigint,
1756
+ incentiveId: bigint,
1757
+ topupAmount: bigint,
1758
+ params?: WriteParams,
1759
+ ) {
1760
+ const boost = await this.getBoost(boostId, params);
1761
+ if (incentiveId >= boost.incentives.length) {
1762
+ throw new Error(`Incentive ID ${incentiveId} out of range`);
1763
+ }
1764
+ const incentive = boost.incentives[Number(incentiveId)];
1765
+ if (!incentive) {
1766
+ throw new Error(`Incentive with ID ${incentiveId} not found`);
1767
+ }
1768
+
1769
+ if (!(topupAmount > 0)) {
1770
+ throw new Error('Top-up amount must be greater than zero');
1771
+ }
1772
+
1773
+ const incentiveData = await incentive.getTopupPayload(topupAmount);
1774
+
1775
+ return await this.topupIncentiveFromSenderRaw(
1776
+ boostId,
1777
+ incentiveId,
1778
+ incentiveData,
1779
+ params,
1780
+ );
1781
+ }
1782
+
1783
+ /**
1784
+ * Prepares and executes a top-up from the caller (msg.sender), specifying the total
1785
+ * tokens you’re willing to provide (including fee). We'll back-calculate the net
1786
+ * top-up for the incentive.
1787
+ *
1788
+ * @public
1789
+ * @async
1790
+ * @param {bigint} boostId The ID of the Boost
1791
+ * @param {bigint} incentiveId The ID of the incentive within that Boost
1792
+ * @param {any} topupPayload A typed struct for the incentive’s top-up parameters
1793
+ * @param {bigint} totalAmount The entire tokens (top-up + fee)
1794
+ * @param {?WriteParams} [params]
1795
+ * @returns {Promise<{ hash: Hex; result: void }>}
1796
+ */
1797
+ public async topupIncentiveFromSenderPostFee(
1798
+ boostId: bigint,
1799
+ incentiveId: bigint,
1800
+ totalAmount: bigint,
1801
+ params?: WriteParams,
1802
+ ) {
1803
+ const feeBps = await this.protocolFee(params);
1804
+ const topupAmount =
1805
+ (totalAmount * FEE_DENOMINATOR) / (FEE_DENOMINATOR + feeBps);
1806
+
1807
+ return this.topupIncentiveFromSenderPreFee(
1808
+ boostId,
1809
+ incentiveId,
1810
+ topupAmount,
1811
+ params,
1812
+ );
1813
+ }
1814
+
1815
+ /**
1816
+ * A lower-level function that actually calls `topupIncentiveFromSender` on-chain,
1817
+ * passing the final total. The contract modifies its internal logic to split net top-up vs. fee.
1818
+ *
1819
+ * @private
1820
+ * @param {bigint} boostId
1821
+ * @param {bigint} incentiveId
1822
+ * @param {Hex} preflightData The raw ABudget.Transfer data from preflight
1823
+ * @param {?WriteParams} [params]
1824
+ * @returns {Promise<{ hash: Hex; result: void }>}
1825
+ */
1826
+ private async topupIncentiveFromSenderRaw(
1827
+ boostId: bigint,
1828
+ incentiveId: bigint,
1829
+ preflightData: Hex,
1830
+ params?: WriteParams,
1831
+ ) {
1832
+ const { request, result } = await simulateBoostCoreTopupIncentiveFromSender(
1833
+ this._config,
1834
+ {
1835
+ ...this.optionallyAttachAccount(),
1836
+ ...(params as object),
1837
+ address: this.assertValidAddress(),
1838
+ args: [boostId, incentiveId, preflightData],
1839
+ },
1840
+ );
1841
+ const hash = await writeBoostCoreTopupIncentiveFromSender(
1842
+ this._config,
1843
+ request,
1844
+ );
1845
+ return { hash, result };
1846
+ }
1847
+
1848
+ /**
1849
+ * A lower-level function that actually calls `topupIncentiveFromBudget` on-chain,
1850
+ * passing the preflight data plus the final total. The contract itself modifies
1851
+ * the amount to (topup + fee).
1852
+ *
1853
+ * @private
1854
+ * @param {bigint} boostId
1855
+ * @param {bigint} incentiveId
1856
+ * @param {Hex} preflightData The raw ABudget.Transfer (encoded) from preflight
1857
+ * @param {Address} [budget] Optional override for the budget
1858
+ * @param {?WriteParams} [params]
1859
+ * @returns {Promise<{ hash: Hex; result: void }>}
1860
+ */
1861
+ private async topupIncentiveFromBudgetRaw(
1862
+ boostId: bigint,
1863
+ incentiveId: bigint,
1864
+ preflightData: Hex,
1865
+ budget?: Address,
1866
+ params?: WriteParams,
1867
+ ) {
1868
+ // e.g. run "simulate + write" pattern
1869
+ const { request, result } = await simulateBoostCoreTopupIncentiveFromBudget(
1870
+ this._config,
1871
+ {
1872
+ ...this.optionallyAttachAccount(),
1873
+ ...(params as object),
1874
+ address: this.assertValidAddress(),
1875
+ args: [boostId, incentiveId, preflightData, budget ?? zeroAddress],
1876
+ },
1877
+ );
1878
+ const hash = await writeBoostCoreTopupIncentiveFromBudget(
1879
+ this._config,
1880
+ request,
1881
+ );
1882
+ return { hash, result };
1883
+ }
1657
1884
  }
@@ -330,6 +330,23 @@ export class AllowListIncentive extends DeployableTarget<
330
330
  };
331
331
  }
332
332
 
333
+ /**
334
+ * Generates a top-up payload for the AllowListIncentive contract.
335
+ *
336
+ * @public
337
+ * @param {bigint} netAmount The net number of slots to be added to the allowlist.
338
+ * @returns {Hex} The ABI-encoded top-up payload.
339
+ */
340
+ public getTopupPayload(netAmount: bigint): Hex {
341
+ return encodeAbiParameters(
342
+ [
343
+ { type: 'address', name: 'allowList' },
344
+ { type: 'uint256', name: 'limit' },
345
+ ],
346
+ [this.payload?.allowList ?? zeroHash, netAmount],
347
+ );
348
+ }
349
+
333
350
  /**
334
351
  * Builds the claim data for the AllowListIncentive.
335
352
  *
@@ -482,6 +482,37 @@ export class CGDAIncentive extends DeployableTarget<
482
482
  return encodeAbiParameters([{ type: 'uint256' }], [amount]);
483
483
  }
484
484
 
485
+ /**
486
+ * Generates a top-up payload for the CGDAIncentive contract.
487
+ *
488
+ * In this approach, we treat a "top-up" as incrementing the existing `totalBudget`
489
+ * in the incentive by `netAmount`. The entire payload is re-encoded with the updated budget.
490
+ *
491
+ * @public
492
+ * @param {bigint} netAmount The additional tokens to add to `totalBudget`.
493
+ * @returns {Hex} The ABI-encoded, updated CGDAIncentive payload.
494
+ */
495
+ public async getTopupPayload(netAmount: bigint): Promise<Hex> {
496
+ return encodeAbiParameters(
497
+ [
498
+ { type: 'address', name: 'asset' },
499
+ { type: 'uint256', name: 'initialReward' },
500
+ { type: 'uint256', name: 'rewardDecay' },
501
+ { type: 'uint256', name: 'rewardBoost' },
502
+ { type: 'uint256', name: 'totalBudget' },
503
+ { type: 'address', name: 'manager' },
504
+ ],
505
+ [
506
+ (await this.asset()) ?? zeroHash,
507
+ this.payload?.initialReward ?? 0n,
508
+ this.payload?.rewardDecay ?? 0n,
509
+ this.payload?.rewardBoost ?? 0n,
510
+ netAmount,
511
+ this.payload?.manager ?? zeroHash,
512
+ ],
513
+ );
514
+ }
515
+
485
516
  /**
486
517
  * Builds the claim data for the CGDAIncentive.
487
518
  *
@@ -502,6 +502,33 @@ export class ERC20Incentive extends DeployableTarget<
502
502
  return encodeAbiParameters([{ type: 'uint256' }], [amount]);
503
503
  }
504
504
 
505
+ /**
506
+ * A helper that composes a typed "top-up" parameter object
507
+ * given a net top-up amount. You can name it whatever you like
508
+ * (e.g. topupParamsFromNetAmount, getTopupPayload, etc.).
509
+ *
510
+ * @public
511
+ * @param {bigint} netAmount The net top-up tokens the user wants to add
512
+ * @param {?WriteParams} [params] (optional) if you need them
513
+ * @returns {Hex} the ABI-encoded data for top-up
514
+ */
515
+ public async getTopupPayload(netAmount: bigint): Promise<Hex> {
516
+ // 1. Build a typed object matching your `ERC20IncentivePayload`.
517
+ // For example, you might want to increment `limit` by `netAmount`,
518
+ // or set `reward = netAmount`. That’s up to your logic:
519
+ const asset = await this.asset();
520
+ const typed: ERC20IncentivePayload = {
521
+ asset: asset || zeroAddress,
522
+ strategy: StrategyType.POOL, // e.g. StrategyType.POOL
523
+ reward: 1n, // store net top-up as the "reward"
524
+ limit: netAmount, // or maybe add netAmount to existing limit
525
+ manager: this.payload?.manager || zeroAddress,
526
+ };
527
+
528
+ // 2. Encode it with your existing `prepareERC20IncentivePayload(...)`.
529
+ return prepareERC20IncentivePayload(typed);
530
+ }
531
+
505
532
  /**
506
533
  * Builds the claim data for the ERC20Incentive.
507
534
  *
@@ -478,6 +478,24 @@ export class ERC20PeggedIncentive extends DeployableTarget<
478
478
  [rewardAmount],
479
479
  );
480
480
  }
481
+ /**
482
+ * Generates a top-up payload for the ERC20PeggedIncentive contract by incrementing
483
+ * the existing `limit` field by `netAmount`. The entire payload is then re-encoded
484
+ * via `prepareERC20PeggedIncentivePayload(...)`.
485
+ *
486
+ * @public
487
+ * @param {bigint} netAmount - The additional limit to add to this incentive.
488
+ * @returns {Hex} The ABI-encoded payload with the updated `limit`.
489
+ */
490
+ public async getTopupPayload(netAmount: bigint): Promise<Hex> {
491
+ return prepareERC20PeggedIncentivePayload({
492
+ asset: (await this.asset()) ?? zeroAddress,
493
+ peg: this.payload?.peg ?? zeroAddress,
494
+ reward: this.payload?.reward ?? 0n,
495
+ limit: netAmount,
496
+ manager: this.payload?.manager ?? zeroAddress,
497
+ });
498
+ }
481
499
 
482
500
  /**
483
501
  * Decodes claim data for the ERC20PeggedIncentive, returning the claim amount.
@@ -613,6 +613,32 @@ export class ERC20PeggedVariableCriteriaIncentive extends DeployableTarget<
613
613
  }
614
614
  }
615
615
 
616
+ /**
617
+ * Generates a top-up payload for the ERC20PeggedIncentive contract by incrementing
618
+ * the existing `limit` field by `netAmount`. The entire payload is then re-encoded
619
+ * via `prepareERC20PeggedIncentivePayload(...)`.
620
+ *
621
+ * @public
622
+ * @param {bigint} netAmount - The additional limit to add to this incentive.
623
+ * @returns {Hex} The ABI-encoded payload with the updated `limit`.
624
+ */
625
+ public async getTopupPayload(netAmount: bigint): Promise<Hex> {
626
+ return prepareERC20PeggedVariableCriteriaIncentivePayload({
627
+ asset: (await this.asset()) ?? zeroAddress,
628
+ peg: this.payload?.peg ?? zeroAddress,
629
+ reward: this.payload?.reward ?? 0n,
630
+ limit: netAmount,
631
+ maxReward: this.payload?.maxReward ?? 0n,
632
+ manager: this.payload?.manager ?? zeroAddress,
633
+ criteria: this.payload?.criteria ?? {
634
+ criteriaType: 0,
635
+ signature: zeroAddress,
636
+ fieldIndex: 0,
637
+ targetContract: zeroAddress,
638
+ },
639
+ });
640
+ }
641
+
616
642
  /**
617
643
  * @inheritdoc
618
644
  *
@@ -22,6 +22,7 @@ import {
22
22
  type Hex,
23
23
  decodeAbiParameters,
24
24
  encodeAbiParameters,
25
+ zeroAddress,
25
26
  } from 'viem';
26
27
  import { ERC20VariableIncentive as ERC20VariableIncentiveBases } from '../../dist/deployments.json';
27
28
  import type {
@@ -394,6 +395,23 @@ export class ERC20VariableIncentive<
394
395
  ]);
395
396
  return limit - totalClaimed;
396
397
  }
398
+ /**
399
+ * Generates a top-up payload for the ERC20PeggedIncentive contract by incrementing
400
+ * the existing `limit` field by `netAmount`. The entire payload is then re-encoded
401
+ * via `prepareERC20PeggedIncentivePayload(...)`.
402
+ *
403
+ * @public
404
+ * @param {bigint} netAmount - The additional limit to add to this incentive.
405
+ * @returns {Hex} The ABI-encoded payload with the updated `limit`.
406
+ */
407
+ public async getTopupPayload(netAmount: bigint): Promise<Hex> {
408
+ return prepareERC20VariableIncentivePayload({
409
+ asset: (await this.asset()) as Address,
410
+ reward: netAmount,
411
+ manager: zeroAddress,
412
+ limit: 1n,
413
+ });
414
+ }
397
415
 
398
416
  /**
399
417
  * Builds the claim data for the ERC20VariableIncentive.
@@ -348,6 +348,28 @@ export class PointsIncentive extends DeployableTarget<
348
348
  };
349
349
  }
350
350
 
351
+ /**
352
+ * Generates a top-up payload for the PointsIncentive contract.
353
+ *
354
+ * @public
355
+ * @param {bigint} netAmount The net reward amount to be added to the incentive.
356
+ * @returns {Hex} The ABI-encoded top-up payload.
357
+ */
358
+ public getTopupPayload(netAmount: bigint): Hex {
359
+ return encodeAbiParameters(
360
+ [
361
+ { type: 'address', name: 'venue' },
362
+ { type: 'bytes4', name: 'selector' },
363
+ { type: 'uint256', name: 'amount' },
364
+ ],
365
+ [
366
+ this.payload?.venue ?? zeroHash,
367
+ this.payload?.selector ?? zeroHash,
368
+ netAmount,
369
+ ],
370
+ );
371
+ }
372
+
351
373
  /**
352
374
  * Builds the claim data for the PointsIncentive.
353
375
  *