The ERC20 Variable Criteria Incentive is a protocol incentive that allows for a variable distribution of native and ERC-20 assets, with each claim amount being determined dynamically at claim time depending on a value extracted from a provided transaction.
On Boost creation, a predetermined amount of the specified asset is transferred into this incentive from the associated budget.
The Boost V2 Docs are under active development and will be subject to changes.
Key Features
- Supports native assets as well as any ERC20
- Rewards dynamic at claim time, depending on the value of a field extracted from a transaction log or function argument.
Unlike budgets, allowlists, and validators, incentives cannot be re-used between multiple Boosts.
Create a new ERC20VariableCriteriaIncentive
import { events as eventSelectors } from '@boostxyz/signatures/events'
import { BoostCore } from '@boostxyz/sdk/BoostCore'
import { selectors as eventSelectors } from "@boostxyz/signatures/events";
import { config } from "./config";
const core = new BoostCore({ config });
await core.createBoost({
maxParticipants: 10n,
budget,
action,
allowList,
incentives: [
core.ERC20VariableCriteriaIncentive({
asset: '0xERC20' || zeroAddress, // use zero address for native assets
reward: parseEther('0.5'), // Amount to multiply a claim amount by, if 0n or 1 ether, user will be transferred the whole amount asserted at claim time.
limit: parseEther('10'), // The total budget allocated to this incentive
maxReward: parseEther('5'), // The maximum amount that can be claimed at a time, if the amount extracted from the transaction given the criteria is more than this value, then the claim value will be this max reward value.
criteria: {
criteriaType: SignatureType.EVENT,
signature: eventSelectors[
"Transfer(address indexed,address indexed,uint256 indexed)"
] as Hex, // Function selector for mint
fieldIndex: 2, // Field index where the dynamic claim value resides
targetContract: '0xERC721',
}
}),
],
});
Get an existing ERC20VariableCriteriaIncentive
// if an incentive address is known, directly construct it
const incentive = core.ERC20VariableCriteriaIncentive("0xc55F719709bDad022B320E76f9DfF7e6F5680767")
// or if you want a budget from a specific chain
const incentiveOnBase = core.ERC20VariableCriteriaIncentive(
"0xc55F719709bDad022B320E76f9DfF7e6F5680767",
{ chainId: 8453 }
)
// or accessible off of a a pre-exiting Boost
const boost = await core.getBoost(0n)
const incentive = boost.incentives.find(incentive => incentive instanceof ERC20VariableCriteriaIncentive)
Setting Up Incentive Criteria
The incentive criteria determines how the reward amount is extracted from transaction data. You can configure it to read values from either contract events or function calls.
Criteria Configuration
The criteria object requires four fields:
criteriaType
: Either SignatureType.EVENT
for event logs or SignatureType.FUNC
for function calls
signature
: The event or function signature to match against
fieldIndex
: Which parameter in the event/function to extract the value from (0-based index)
targetContract
: The contract address to watch for the event/function
You can use the @boostxyz/signatures
package to get pre-defined event signatures instead of using raw hex values. For example: import { selectors } from '@boostxyz/signatures/events'
Example: Event-based Criteria
Here’s an example setting up criteria to extract an amount from a Transfer event:
import { selectors } from '@boostxyz/signatures/events'
import { SignatureType } from '@boostxyz/sdk'
const criteria = {
criteriaType: SignatureType.EVENT,
// Use pre-defined signature for Transfer(address,address,uint256)
signature: selectors['Transfer(address indexed,address indexed,uint256)'],
fieldIndex: 2, // Extract amount from the third parameter
targetContract: '0xTokenContract' // Contract to watch for events
}
Example: Function-based Criteria
Here’s an example setting up criteria to extract an amount from a mint function:
import { selectors } from '@boostxyz/signatures/functions'
import { SignatureType } from '@boostxyz/sdk/claiming'
const criteria = {
criteriaType: SignatureType.FUNC,
// Use pre-defined signature for mint(uint256)
signature: selectors['mint(uint256)'],
fieldIndex: 0, // Extract amount from the first parameter (quantity)
targetContract: '0xNFTContract' // Contract to watch for function calls
}
The extracted value will be used to calculate the reward amount, which is then capped by the maxReward
parameter if necessary.
Sign a claim amount
// Get the index of the incentive to claim
const incentiveIndex = boost.incentives.findIndex(incentive => incentive instanceof ERC20VariableCriteriaIncentive)
// build claim data, signing a variable amount for the incentive
const allClaimData = boost.incentives.map(incentive => {
if (incentive instanceof ERC20VariableCriteriaIncentive || incentive instanceof ERC20VariableCriteriaIncentive) {
const rewardAmount = parseEther("1");
return incentive.buildClaimData(rewardAmount);
} else {
return ''
}
})
// Sign a valid transaction with the variable reward amount
const { data } = await axios.get('/signatures', {
params: {
boostId: `${chainId}:${boostCoreAddress}:${boost.id}`,
txHash,
claimData: allClaimData.join(',')
}
});
// claim the ERC20VariableCriteriaIncentive
const { signature, incentiveId, claimant } = data.at(incentiveIndex);
await core.claimIncentive(
boost.id,
incentiveId,
claimant,
signature,
);