@boostxyz/sdk 5.0.0 → 5.1.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/dist/Actions/Action.cjs +1 -1
- package/dist/Actions/Action.js +5 -5
- package/dist/Actions/EventAction.cjs +1 -1
- package/dist/Actions/EventAction.cjs.map +1 -1
- package/dist/Actions/EventAction.d.ts +18 -0
- package/dist/Actions/EventAction.d.ts.map +1 -1
- package/dist/Actions/EventAction.js +201 -132
- package/dist/Actions/EventAction.js.map +1 -1
- package/dist/AllowLists/AllowList.cjs +1 -1
- package/dist/AllowLists/AllowList.js +3 -3
- package/dist/AllowLists/SimpleAllowList.cjs +1 -1
- package/dist/AllowLists/SimpleAllowList.js +8 -8
- 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 +14 -0
- package/dist/BoostCore.d.ts.map +1 -1
- package/dist/BoostCore.js +94 -75
- package/dist/BoostCore.js.map +1 -1
- package/dist/BoostRegistry.cjs +1 -1
- package/dist/BoostRegistry.js +6 -6
- package/dist/{Budget-fHCvlPfB.js → Budget-CTxrE06r.js} +53 -53
- package/dist/{Budget-fHCvlPfB.js.map → Budget-CTxrE06r.js.map} +1 -1
- package/dist/{Budget-Cnyh81n5.cjs → Budget-Cui2BUr4.cjs} +2 -2
- package/dist/{Budget-Cnyh81n5.cjs.map → Budget-Cui2BUr4.cjs.map} +1 -1
- package/dist/Budgets/Budget.cjs +1 -1
- package/dist/Budgets/Budget.js +3 -3
- package/dist/Budgets/ManagedBudget.cjs +1 -1
- package/dist/Budgets/ManagedBudget.js +29 -29
- 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 +15 -15
- package/dist/Incentive-AWwdbmix.cjs +2 -0
- package/dist/Incentive-AWwdbmix.cjs.map +1 -0
- package/dist/Incentive-CsFJZxYl.js +901 -0
- package/dist/Incentive-CsFJZxYl.js.map +1 -0
- package/dist/Incentives/AllowListIncentive.cjs +1 -1
- package/dist/Incentives/AllowListIncentive.js +11 -11
- package/dist/Incentives/CGDAIncentive.cjs +1 -1
- package/dist/Incentives/CGDAIncentive.cjs.map +1 -1
- package/dist/Incentives/CGDAIncentive.d.ts +1 -1
- package/dist/Incentives/CGDAIncentive.d.ts.map +1 -1
- package/dist/Incentives/CGDAIncentive.js +24 -22
- package/dist/Incentives/CGDAIncentive.js.map +1 -1
- package/dist/Incentives/ERC20Incentive.cjs +1 -1
- package/dist/Incentives/ERC20Incentive.cjs.map +1 -1
- package/dist/Incentives/ERC20Incentive.js +19 -19
- package/dist/Incentives/ERC20Incentive.js.map +1 -1
- package/dist/Incentives/ERC20PeggedVariableCriteriaIncentive.d.ts +1035 -0
- package/dist/Incentives/ERC20PeggedVariableCriteriaIncentive.d.ts.map +1 -0
- package/dist/Incentives/ERC20VariableCriteriaIncentive.cjs +1 -1
- package/dist/Incentives/ERC20VariableCriteriaIncentive.cjs.map +1 -1
- package/dist/Incentives/ERC20VariableCriteriaIncentive.js +19 -19
- package/dist/Incentives/ERC20VariableCriteriaIncentive.js.map +1 -1
- package/dist/Incentives/ERC20VariableIncentive.cjs +1 -1
- package/dist/Incentives/ERC20VariableIncentive.cjs.map +1 -1
- package/dist/Incentives/ERC20VariableIncentive.d.ts +1 -1
- package/dist/Incentives/ERC20VariableIncentive.d.ts.map +1 -1
- package/dist/Incentives/ERC20VariableIncentive.js +25 -23
- package/dist/Incentives/ERC20VariableIncentive.js.map +1 -1
- package/dist/Incentives/Incentive.cjs +1 -1
- package/dist/Incentives/Incentive.d.ts +6 -5
- package/dist/Incentives/Incentive.d.ts.map +1 -1
- package/dist/Incentives/Incentive.js +17 -16
- package/dist/Incentives/PointsIncentive.cjs +1 -1
- package/dist/Incentives/PointsIncentive.js +8 -8
- package/dist/{SimpleDenyList-CgQvS1ek.cjs → SimpleDenyList-DQT0Fj3v.cjs} +2 -2
- package/dist/{SimpleDenyList-CgQvS1ek.cjs.map → SimpleDenyList-DQT0Fj3v.cjs.map} +1 -1
- package/dist/{SimpleDenyList-GZlSeh2G.js → SimpleDenyList-kcqV-01w.js} +12 -12
- package/dist/{SimpleDenyList-GZlSeh2G.js.map → SimpleDenyList-kcqV-01w.js.map} +1 -1
- package/dist/Validators/LimitedSignerValidator.cjs +1 -1
- package/dist/Validators/LimitedSignerValidator.js +18 -18
- package/dist/Validators/SignerValidator.cjs +1 -1
- package/dist/Validators/SignerValidator.js +16 -16
- package/dist/Validators/Validator.cjs +1 -1
- package/dist/Validators/Validator.js +4 -4
- package/dist/componentInterfaces-D7r9xJmt.cjs +2 -0
- package/dist/componentInterfaces-D7r9xJmt.cjs.map +1 -0
- package/dist/componentInterfaces-PR3ijhgZ.js +18 -0
- package/dist/componentInterfaces-PR3ijhgZ.js.map +1 -0
- package/dist/{deployments-CMdsItcq.cjs → deployments-BNFI9vnc.cjs} +2 -2
- package/dist/deployments-BNFI9vnc.cjs.map +1 -0
- package/dist/{deployments-DzhmI-zL.js → deployments-D68FgefY.js} +31 -31
- package/dist/deployments-D68FgefY.js.map +1 -0
- package/dist/deployments.json +20 -15
- package/dist/generated-CRD9XfOT.cjs +3 -0
- package/dist/generated-CRD9XfOT.cjs.map +1 -0
- package/dist/{generated-Hbd0NIf9.js → generated-ClbO_ULI.js} +1274 -709
- package/dist/generated-ClbO_ULI.js.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +133 -129
- package/dist/utils.cjs +1 -1
- package/dist/utils.cjs.map +1 -1
- package/dist/utils.d.ts +7 -1
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +1121 -41
- package/dist/utils.js.map +1 -1
- package/package.json +1 -1
- package/src/Actions/EventAction.ts +85 -1
- package/src/BoostCore.ts +27 -0
- package/src/Incentives/CGDAIncentive.ts +3 -1
- package/src/Incentives/ERC20PeggedVariableCriteriaIncentive.test.ts +315 -0
- package/src/Incentives/ERC20PeggedVariableCriteriaIncentive.ts +728 -0
- package/src/Incentives/ERC20VariableIncentive.ts +3 -1
- package/src/Incentives/Incentive.ts +8 -2
- package/src/index.ts +1 -0
- package/src/utils.ts +10 -0
- package/dist/Incentive-M45PaQsT.js +0 -397
- package/dist/Incentive-M45PaQsT.js.map +0 -1
- package/dist/Incentive-axLU7ndr.cjs +0 -2
- package/dist/Incentive-axLU7ndr.cjs.map +0 -1
- package/dist/componentInterfaces-Bt-4sNB5.cjs +0 -2
- package/dist/componentInterfaces-Bt-4sNB5.cjs.map +0 -1
- package/dist/componentInterfaces-C4uAYjSo.js +0 -16
- package/dist/componentInterfaces-C4uAYjSo.js.map +0 -1
- package/dist/deployments-CMdsItcq.cjs.map +0 -1
- package/dist/deployments-DzhmI-zL.js.map +0 -1
- package/dist/generated-CNkDfjzI.cjs +0 -3
- package/dist/generated-CNkDfjzI.cjs.map +0 -1
- package/dist/generated-Hbd0NIf9.js.map +0 -1
package/dist/utils.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.js","sources":["../src/utils.ts"],"sourcesContent":["import {\n type Config,\n type ReadContractParameters,\n type SimulateContractParameters,\n type WatchContractEventParameters,\n getAccount,\n getChainId,\n readContract,\n waitForTransactionReceipt,\n} from '@wagmi/core';\nimport type { ExtractAbiEvent } from 'abitype';\nimport type {\n Abi,\n AbiEvent,\n Address,\n ContractEventName,\n ContractFunctionName,\n GetLogsParameters,\n Hash,\n Hex,\n Log,\n WaitForTransactionReceiptParameters,\n} from 'viem';\nimport { isHex, keccak256, slice, toHex } from 'viem';\nimport {\n InvalidProtocolChainIdError,\n NoContractAddressUponReceiptError,\n} from './errors';\n\nexport type Overwrite<T, U> = Pick<T, Exclude<keyof T, keyof U>> & U;\n\n/**\n * Enum encapsulating all the different types of targets used in the Boost V2 Protocol.\n *\n * @export\n * @enum {number}\n */\nexport enum RegistryType {\n ACTION = 0,\n ALLOW_LIST = 1,\n BUDGET = 2,\n INCENTIVE = 3,\n VALIDATOR = 4,\n}\n\n/**\n * Enum encapsulating all the different cheat codes for criteria modules.\n * An Event Action action step criteria with a `fieldIndex` set to `CheatCodes.ANY_ACTION_PARAM`, fieldType set to `PrimitiveType.BYTES`, and `filterType` set to `FilterType.EQUAL` will always return true when validating the field, regardless of inputs\n * An Event Action `ActionClaimant` with a `fieldIndex` set to `CheatCodes.TX_SENDER_CLAIMANT`, will always validate with the sender of a given transaction.\n *\n *\n * @export\n * @enum {number}\n */\nexport enum CheatCodes {\n /* An Event Action action step criteria with a `fieldIndex` set to `CheatCodes.ANY_ACTION_PARAM`, fieldType set to `PrimitiveType.BYTES`, and `filterType` set to `FilterType.EQUAL` will always return true when validating the field, regardless of inputs */\n ANY_ACTION_PARAM = 255,\n /* An Event Action `ActionClaimant` with a `fieldIndex` set to `CheatCodes.TX_SENDER_CLAIMANT`, will always validate with the sender of a given transaction. */\n TX_SENDER_CLAIMANT = 255,\n /* For use with `ERC20VariableCriteriaIncentive`, if the criteria's `fieldIndex` is set to `CheatCodes.GAS_REBATE_INCENTIVE`, will claim an incentive amount equal to the transaction's gas used. */\n GAS_REBATE_INCENTIVE = 255,\n}\n\n/**\n * Helper type that encapsulates common writeContract parameters without fields like `abi`, `args`, `functionName`, `address` that are expected to be provided the SDK.\n * See (writeContract)[https://viem.sh/docs/contract/writeContract]\n *\n * @export\n * @typedef {WriteParams}\n * @template {Abi} abi\n * @template {ContractFunctionName<abi>} functionName\n */\nexport type WriteParams = Partial<\n Omit<SimulateContractParameters, 'address' | 'args' | 'functionName' | 'abi'>\n>;\n\n/**\n * Helper type that encapsulates common readContract parameters without fields like `abi`, `args`, `functionName`, `address` that are expected to be provided the SDK.\n * See (readContract)[https://viem.sh/docs/contract/readContract]\n *\n * @export\n * @typedef {ReadParams}\n * @template {Abi} abi\n * @template {ContractFunctionName<abi>} functionName\n */\nexport type ReadParams = Partial<\n Omit<ReadContractParameters, 'address' | 'args' | 'functionName' | 'abi'>\n>;\n\n/**\n * Helper type that encapsulates common `watchContractEvent` parameters without fields like `address`, and `abi` that are expected to be provided the SDK.\n * See (watchContractEvent)[https://wagmi.sh/core/api/actions/watchContractEvent]\n *\n * @export\n * @typedef {WatchParams}\n * @template {Abi} abi\n * @template {ContractEventName<abi> | undefined} [eventName=undefined]\n */\nexport type WatchParams<\n abi extends Abi,\n eventName extends ContractEventName<abi> | undefined = undefined,\n> = Partial<\n Omit<WatchContractEventParameters<abi, eventName>, 'address' | 'abi'>\n>;\n\n/**\n * Helper type that encapsulates common `getLogs` parameters without fields like `address` that are expected to be provided the SDK.\n * See (getLogs)[https://viem.sh/docs/actions/public/getLogs#getlogs]\n *\n * @export\n * @typedef {GetLogsParams}\n * @template {Abi} abi\n * @template {ContractEventName<abi>} event\n * @template {ExtractAbiEvent<abi, event>} [abiEvent=ExtractAbiEvent<abi, event>]\n * @template {| readonly AbiEvent[]\n * | readonly unknown[]\n * | undefined} [abiEvents=abiEvent extends AbiEvent ? [abiEvent] : undefined]\n */\nexport type GetLogsParams<\n abi extends Abi,\n event extends ContractEventName<abi>,\n abiEvent extends ExtractAbiEvent<abi, event> = ExtractAbiEvent<abi, event>,\n abiEvents extends\n | readonly AbiEvent[]\n | readonly unknown[]\n | undefined = abiEvent extends AbiEvent ? [abiEvent] : undefined,\n> = Partial<Omit<GetLogsParameters<abiEvent, abiEvents>, 'address'>> & {\n chainId?: number | undefined;\n};\n\n/**\n * A generic `viem.Log` event with typed `args` support via a given `Abi` and `ContractEventName`\n *\n * @export\n * @typedef {GenericLog}\n * @template {Abi} abi\n * @template {ContractEventName<abi>} [event=ContractEventName<abi>]\n */\nexport type GenericLog<\n abi extends Abi,\n event extends ContractEventName<abi> = ContractEventName<abi>,\n> = Log<bigint, number, false, ExtractAbiEvent<abi, event>, false>;\n\n/**\n * Helper utility to convert a string to a `bytes4` type\n *\n * @export\n * @param {string} input\n * @returns {Hex}\n */\nexport function bytes4(input: string) {\n return slice(isHex(input) ? keccak256(input) : keccak256(toHex(input)), 0, 4);\n}\n\n/**\n * Utility function to wait for a transaction receipt, and extract the contractAddress\n *\n * @export\n * @async\n * @param {WagmiConfig} config - [Wagmi Configuration](https://wagmi.sh/core/api/createConfig)\n * @param {Promise<Hash>} hash - A transaction hash promise\n * @param {?Omit<WaitForTransactionReceiptParameters, 'hash'>} [waitParams] - @see {@link WaitForTransactionReceiptParameters}\n * @returns {Promise<Address>}\n * @throws {@link NoContractAddressUponReceiptError} if no `contractAddress` exists after the transaction has been received\n */\nexport async function getDeployedContractAddress(\n config: Config,\n hash: Promise<Hash>,\n waitParams?: Omit<WaitForTransactionReceiptParameters, 'hash'>,\n) {\n const receipt = await waitForTransactionReceipt(config, {\n ...waitParams,\n hash: await hash,\n });\n if (!receipt.contractAddress)\n throw new NoContractAddressUponReceiptError(receipt);\n return receipt.contractAddress;\n}\n\n/**\n * Utility type to encapsulate a transaction hash, and the simulated result prior to submitting the transaction.\n *\n * @export\n * @typedef {HashAndSimulatedResult}\n * @template [T=unknown]\n */\nexport type HashAndSimulatedResult<T = unknown> = { hash: Hash; result: T };\n\n/**\n * Helper function to wait for a transaction receipt given a hash promise.\n *\n * @export\n * @async\n * @template [Result=unknown]\n * @param {WagmiConfig} config\n * @param {Promise<HashAndSimulatedResult<Result>>} hashPromise\n * @param {?Omit<WaitForTransactionReceiptParameters, 'hash'>} [waitParams]\n * @returns {Promise<Result>}\n */\nexport async function awaitResult<Result = unknown>(\n config: Config,\n hashPromise: Promise<HashAndSimulatedResult<Result>>,\n waitParams?: Omit<WaitForTransactionReceiptParameters, 'hash'>,\n): Promise<Result> {\n const { hash, result } = await hashPromise;\n await waitForTransactionReceipt(config, {\n ...waitParams,\n hash,\n });\n return result;\n}\n\n/**\n * Given a wagmi config and a map of chain id's to addresses, determine an address/chainId combo that maps to the currently connected chain id, or throw a typed error.\n *\n * @export\n * @param {Config} config\n * @param {Record<string, Address>} addressByChainId\n * @param {number} desiredChainId\n * @returns {{ chainId: number, address: Address }}\n * @throws {@link InvalidProtocolChainIdError}\n */\nexport function assertValidAddressByChainId(\n config: Config,\n addressByChainId: Record<number, Address>,\n desiredChainId?: number,\n): { chainId: number; address: Address } {\n let chainId: number | undefined = undefined;\n\n const wagmiChainId = getChainId(config);\n if (wagmiChainId && addressByChainId[wagmiChainId]) chainId = wagmiChainId;\n // if manually providing a chain id for some contract operation, try to use it\n if (desiredChainId !== undefined) {\n if (addressByChainId[desiredChainId]) chainId = desiredChainId;\n } else if (wagmiChainId !== undefined) {\n // otherwise if we can get the current chain id off the connected account and it matches one of ours, use it\n if (addressByChainId[wagmiChainId]) chainId = wagmiChainId;\n }\n // chainId is still undefined, try to get chain id off viem client\n if (chainId === undefined) {\n const wagmiAccount = getAccount(config);\n if (wagmiAccount.chainId !== undefined) {\n // otherwise if we can get the current chain id off the connected account and it matches one of ours, use it\n if (addressByChainId[wagmiAccount.chainId])\n chainId = wagmiAccount.chainId;\n }\n }\n // if chainId is STILL undefined, use our default addresses\n // TODO: update this when on prod network\n if (chainId === undefined) chainId = Number(__DEFAULT_CHAIN_ID__);\n if (!addressByChainId[chainId])\n throw new InvalidProtocolChainIdError(\n chainId,\n Object.keys(addressByChainId).map(Number),\n );\n // biome-ignore lint/style/noNonNullAssertion: this type should be narrowed by the above statement but isn't?\n return { chainId, address: addressByChainId[chainId]! };\n}\n\n/**\n * Check an ERC20's balance for a given asset and\n *\n * @public\n * @async\n * @param {Config} [config]\n * @param {Address} [asset]\n * @param {Address} [owner]\n * @param {?ReadParams} [params]\n * @returns {Promise<bigint>} - The erc20 balance\n */\nexport async function getErc20Balance(\n config: Config,\n asset: Address,\n owner: Address,\n params?: ReadParams,\n) {\n return await readContract(config, {\n ...params,\n functionName: 'balanceOf',\n address: asset,\n args: [owner],\n abi: [\n {\n constant: true,\n inputs: [\n {\n name: '_owner',\n type: 'address',\n },\n ],\n name: 'balanceOf',\n outputs: [\n {\n name: 'balance',\n type: 'uint256',\n },\n ],\n stateMutability: 'view',\n type: 'function',\n },\n ],\n });\n}\n"],"names":["RegistryType","CheatCodes","bytes4","input","slice","isHex","keccak256","toHex","getDeployedContractAddress","config","hash","waitParams","receipt","waitForTransactionReceipt","NoContractAddressUponReceiptError","awaitResult","hashPromise","result","assertValidAddressByChainId","addressByChainId","desiredChainId","chainId","wagmiChainId","getChainId","wagmiAccount","getAccount","InvalidProtocolChainIdError","getErc20Balance","asset","owner","params","readContract"],"mappings":";;;AAqCY,IAAAA,sBAAAA,OACVA,EAAAA,EAAA,SAAS,CAAT,IAAA,UACAA,EAAAA,EAAA,aAAa,CAAb,IAAA,cACAA,EAAAA,EAAA,SAAS,CAAT,IAAA,UACAA,EAAAA,EAAA,YAAY,CAAZ,IAAA,aACAA,EAAAA,EAAA,YAAY,CAAZ,IAAA,aALUA,IAAAA,KAAA,CAAA,CAAA,GAiBAC,sBAAAA,OAEVA,EAAAA,EAAA,mBAAmB,GAAnB,IAAA,oBAEAA,EAAAA,EAAA,qBAAqB,GAArB,IAAA,sBAEAA,EAAAA,EAAA,uBAAuB,GAAvB,IAAA,wBANUA,IAAAA,KAAA,CAAA,CAAA;AAgGL,SAASC,EAAOC,GAAe;AACpC,SAAOC,EAAMC,EAAMF,CAAK,IAAIG,EAAUH,CAAK,IAAIG,EAAUC,EAAMJ,CAAK,CAAC,GAAG,GAAG,CAAC;AAC9E;AAasB,eAAAK,EACpBC,GACAC,GACAC,GACA;AACM,QAAAC,IAAU,MAAMC,EAA0BJ,GAAQ;AAAA,IACtD,GAAGE;AAAA,IACH,MAAM,MAAMD;AAAA,EAAA,CACb;AACD,MAAI,CAACE,EAAQ;AACL,UAAA,IAAIE,EAAkCF,CAAO;AACrD,SAAOA,EAAQ;AACjB;AAsBsB,eAAAG,EACpBN,GACAO,GACAL,GACiB;AACjB,QAAM,EAAE,MAAAD,GAAM,QAAAO,EAAO,IAAI,MAAMD;AAC/B,eAAMH,EAA0BJ,GAAQ;AAAA,IACtC,GAAGE;AAAA,IACH,MAAAD;AAAA,EAAA,CACD,GACMO;AACT;AAYgB,SAAAC,EACdT,GACAU,GACAC,GACuC;AACvC,MAAIC;AAEE,QAAAC,IAAeC,EAAWd,CAAM;AAUtC,MATIa,KAAgBH,EAAiBG,CAAY,MAAaD,IAAAC,IAE1DF,MAAmB,SACjBD,EAAiBC,CAAc,MAAaC,IAAAD,KACvCE,MAAiB,UAEtBH,EAAiBG,CAAY,MAAaD,IAAAC,IAG5CD,MAAY,QAAW;AACnB,UAAAG,IAAeC,EAAWhB,CAAM;AAClC,IAAAe,EAAa,YAAY,UAEvBL,EAAiBK,EAAa,OAAO,MACvCH,IAAUG,EAAa;AAAA,EAE7B;AAII,MADAH,MAAY,WAAqBA,IAAO,WACxC,CAACF,EAAiBE,CAAO;AAC3B,UAAM,IAAIK;AAAA,MACRL;AAAA,MACA,OAAO,KAAKF,CAAgB,EAAE,IAAI,MAAM;AAAA,IAAA;AAG5C,SAAO,EAAE,SAAAE,GAAS,SAASF,EAAiBE,CAAO,EAAG;AACxD;AAaA,eAAsBM,EACpBlB,GACAmB,GACAC,GACAC,GACA;AACO,SAAA,MAAMC,EAAatB,GAAQ;AAAA,IAChC,GAAGqB;AAAA,IACH,cAAc;AAAA,IACd,SAASF;AAAA,IACT,MAAM,CAACC,CAAK;AAAA,IACZ,KAAK;AAAA,MACH;AAAA,QACE,UAAU;AAAA,QACV,QAAQ;AAAA,UACN;AAAA,YACE,MAAM;AAAA,YACN,MAAM;AAAA,UACR;AAAA,QACF;AAAA,QACA,MAAM;AAAA,QACN,SAAS;AAAA,UACP;AAAA,YACE,MAAM;AAAA,YACN,MAAM;AAAA,UACR;AAAA,QACF;AAAA,QACA,iBAAiB;AAAA,QACjB,MAAM;AAAA,MACR;AAAA,IACF;AAAA,EAAA,CACD;AACH;"}
|
|
1
|
+
{"version":3,"file":"utils.js","sources":["../src/utils.ts"],"sourcesContent":["import { events } from '@boostxyz/signatures';\nimport {\n type Config,\n type ReadContractParameters,\n type SimulateContractParameters,\n type WatchContractEventParameters,\n getAccount,\n getChainId,\n readContract,\n waitForTransactionReceipt,\n} from '@wagmi/core';\nimport type { ExtractAbiEvent } from 'abitype';\nimport type {\n Abi,\n AbiEvent,\n Address,\n ContractEventName,\n ContractFunctionName,\n GetLogsParameters,\n Hash,\n Hex,\n Log,\n WaitForTransactionReceiptParameters,\n} from 'viem';\nimport { isHex, keccak256, slice, toHex } from 'viem';\nimport {\n InvalidProtocolChainIdError,\n NoContractAddressUponReceiptError,\n} from './errors';\n\nexport type Overwrite<T, U> = Pick<T, Exclude<keyof T, keyof U>> & U;\n\n/**\n * Enum encapsulating all the different types of targets used in the Boost V2 Protocol.\n *\n * @export\n * @enum {number}\n */\nexport enum RegistryType {\n ACTION = 0,\n ALLOW_LIST = 1,\n BUDGET = 2,\n INCENTIVE = 3,\n VALIDATOR = 4,\n}\n\n/**\n * Enum encapsulating all the different cheat codes for criteria modules.\n * An Event Action action step criteria with a `fieldIndex` set to `CheatCodes.ANY_ACTION_PARAM`, fieldType set to `PrimitiveType.BYTES`, and `filterType` set to `FilterType.EQUAL` will always return true when validating the field, regardless of inputs\n * An Event Action `ActionClaimant` with a `fieldIndex` set to `CheatCodes.TX_SENDER_CLAIMANT`, will always validate with the sender of a given transaction.\n *\n *\n * @export\n * @enum {number}\n */\nexport enum CheatCodes {\n /* An Event Action action step criteria with a `fieldIndex` set to `CheatCodes.ANY_ACTION_PARAM`, fieldType set to `PrimitiveType.BYTES`, and `filterType` set to `FilterType.EQUAL` will always return true when validating the field, regardless of inputs */\n ANY_ACTION_PARAM = 255,\n /* An Event Action `ActionClaimant` with a `fieldIndex` set to `CheatCodes.TX_SENDER_CLAIMANT`, will always validate with the sender of a given transaction. */\n TX_SENDER_CLAIMANT = 255,\n /* For use with `ERC20VariableCriteriaIncentive`, if the criteria's `fieldIndex` is set to `CheatCodes.GAS_REBATE_INCENTIVE`, will claim an incentive amount equal to the transaction's gas used. */\n GAS_REBATE_INCENTIVE = 255,\n}\n\n/**\n * The signature for the Transfer event for both ERC20 and ERC721.\n *\n * @type {Hex}\n */\nexport const TRANSFER_SIGNATURE = events.selectors[\n 'Transfer(address indexed,address indexed,uint256 indexed)'\n] as Hex;\n\n/**\n * Helper type that encapsulates common writeContract parameters without fields like `abi`, `args`, `functionName`, `address` that are expected to be provided the SDK.\n * See (writeContract)[https://viem.sh/docs/contract/writeContract]\n *\n * @export\n * @typedef {WriteParams}\n * @template {Abi} abi\n * @template {ContractFunctionName<abi>} functionName\n */\nexport type WriteParams = Partial<\n Omit<SimulateContractParameters, 'address' | 'args' | 'functionName' | 'abi'>\n>;\n\n/**\n * Helper type that encapsulates common readContract parameters without fields like `abi`, `args`, `functionName`, `address` that are expected to be provided the SDK.\n * See (readContract)[https://viem.sh/docs/contract/readContract]\n *\n * @export\n * @typedef {ReadParams}\n * @template {Abi} abi\n * @template {ContractFunctionName<abi>} functionName\n */\nexport type ReadParams = Partial<\n Omit<ReadContractParameters, 'address' | 'args' | 'functionName' | 'abi'>\n>;\n\n/**\n * Helper type that encapsulates common `watchContractEvent` parameters without fields like `address`, and `abi` that are expected to be provided the SDK.\n * See (watchContractEvent)[https://wagmi.sh/core/api/actions/watchContractEvent]\n *\n * @export\n * @typedef {WatchParams}\n * @template {Abi} abi\n * @template {ContractEventName<abi> | undefined} [eventName=undefined]\n */\nexport type WatchParams<\n abi extends Abi,\n eventName extends ContractEventName<abi> | undefined = undefined,\n> = Partial<\n Omit<WatchContractEventParameters<abi, eventName>, 'address' | 'abi'>\n>;\n\n/**\n * Helper type that encapsulates common `getLogs` parameters without fields like `address` that are expected to be provided the SDK.\n * See (getLogs)[https://viem.sh/docs/actions/public/getLogs#getlogs]\n *\n * @export\n * @typedef {GetLogsParams}\n * @template {Abi} abi\n * @template {ContractEventName<abi>} event\n * @template {ExtractAbiEvent<abi, event>} [abiEvent=ExtractAbiEvent<abi, event>]\n * @template {| readonly AbiEvent[]\n * | readonly unknown[]\n * | undefined} [abiEvents=abiEvent extends AbiEvent ? [abiEvent] : undefined]\n */\nexport type GetLogsParams<\n abi extends Abi,\n event extends ContractEventName<abi>,\n abiEvent extends ExtractAbiEvent<abi, event> = ExtractAbiEvent<abi, event>,\n abiEvents extends\n | readonly AbiEvent[]\n | readonly unknown[]\n | undefined = abiEvent extends AbiEvent ? [abiEvent] : undefined,\n> = Partial<Omit<GetLogsParameters<abiEvent, abiEvents>, 'address'>> & {\n chainId?: number | undefined;\n};\n\n/**\n * A generic `viem.Log` event with typed `args` support via a given `Abi` and `ContractEventName`\n *\n * @export\n * @typedef {GenericLog}\n * @template {Abi} abi\n * @template {ContractEventName<abi>} [event=ContractEventName<abi>]\n */\nexport type GenericLog<\n abi extends Abi,\n event extends ContractEventName<abi> = ContractEventName<abi>,\n> = Log<bigint, number, false, ExtractAbiEvent<abi, event>, false>;\n\n/**\n * Helper utility to convert a string to a `bytes4` type\n *\n * @export\n * @param {string} input\n * @returns {Hex}\n */\nexport function bytes4(input: string) {\n return slice(isHex(input) ? keccak256(input) : keccak256(toHex(input)), 0, 4);\n}\n\n/**\n * Utility function to wait for a transaction receipt, and extract the contractAddress\n *\n * @export\n * @async\n * @param {WagmiConfig} config - [Wagmi Configuration](https://wagmi.sh/core/api/createConfig)\n * @param {Promise<Hash>} hash - A transaction hash promise\n * @param {?Omit<WaitForTransactionReceiptParameters, 'hash'>} [waitParams] - @see {@link WaitForTransactionReceiptParameters}\n * @returns {Promise<Address>}\n * @throws {@link NoContractAddressUponReceiptError} if no `contractAddress` exists after the transaction has been received\n */\nexport async function getDeployedContractAddress(\n config: Config,\n hash: Promise<Hash>,\n waitParams?: Omit<WaitForTransactionReceiptParameters, 'hash'>,\n) {\n const receipt = await waitForTransactionReceipt(config, {\n ...waitParams,\n hash: await hash,\n });\n if (!receipt.contractAddress)\n throw new NoContractAddressUponReceiptError(receipt);\n return receipt.contractAddress;\n}\n\n/**\n * Utility type to encapsulate a transaction hash, and the simulated result prior to submitting the transaction.\n *\n * @export\n * @typedef {HashAndSimulatedResult}\n * @template [T=unknown]\n */\nexport type HashAndSimulatedResult<T = unknown> = { hash: Hash; result: T };\n\n/**\n * Helper function to wait for a transaction receipt given a hash promise.\n *\n * @export\n * @async\n * @template [Result=unknown]\n * @param {WagmiConfig} config\n * @param {Promise<HashAndSimulatedResult<Result>>} hashPromise\n * @param {?Omit<WaitForTransactionReceiptParameters, 'hash'>} [waitParams]\n * @returns {Promise<Result>}\n */\nexport async function awaitResult<Result = unknown>(\n config: Config,\n hashPromise: Promise<HashAndSimulatedResult<Result>>,\n waitParams?: Omit<WaitForTransactionReceiptParameters, 'hash'>,\n): Promise<Result> {\n const { hash, result } = await hashPromise;\n await waitForTransactionReceipt(config, {\n ...waitParams,\n hash,\n });\n return result;\n}\n\n/**\n * Given a wagmi config and a map of chain id's to addresses, determine an address/chainId combo that maps to the currently connected chain id, or throw a typed error.\n *\n * @export\n * @param {Config} config\n * @param {Record<string, Address>} addressByChainId\n * @param {number} desiredChainId\n * @returns {{ chainId: number, address: Address }}\n * @throws {@link InvalidProtocolChainIdError}\n */\nexport function assertValidAddressByChainId(\n config: Config,\n addressByChainId: Record<number, Address>,\n desiredChainId?: number,\n): { chainId: number; address: Address } {\n let chainId: number | undefined = undefined;\n\n const wagmiChainId = getChainId(config);\n if (wagmiChainId && addressByChainId[wagmiChainId]) chainId = wagmiChainId;\n // if manually providing a chain id for some contract operation, try to use it\n if (desiredChainId !== undefined) {\n if (addressByChainId[desiredChainId]) chainId = desiredChainId;\n } else if (wagmiChainId !== undefined) {\n // otherwise if we can get the current chain id off the connected account and it matches one of ours, use it\n if (addressByChainId[wagmiChainId]) chainId = wagmiChainId;\n }\n // chainId is still undefined, try to get chain id off viem client\n if (chainId === undefined) {\n const wagmiAccount = getAccount(config);\n if (wagmiAccount.chainId !== undefined) {\n // otherwise if we can get the current chain id off the connected account and it matches one of ours, use it\n if (addressByChainId[wagmiAccount.chainId])\n chainId = wagmiAccount.chainId;\n }\n }\n // if chainId is STILL undefined, use our default addresses\n // TODO: update this when on prod network\n if (chainId === undefined) chainId = Number(__DEFAULT_CHAIN_ID__);\n if (!addressByChainId[chainId])\n throw new InvalidProtocolChainIdError(\n chainId,\n Object.keys(addressByChainId).map(Number),\n );\n // biome-ignore lint/style/noNonNullAssertion: this type should be narrowed by the above statement but isn't?\n return { chainId, address: addressByChainId[chainId]! };\n}\n\n/**\n * Check an ERC20's balance for a given asset and\n *\n * @public\n * @async\n * @param {Config} [config]\n * @param {Address} [asset]\n * @param {Address} [owner]\n * @param {?ReadParams} [params]\n * @returns {Promise<bigint>} - The erc20 balance\n */\nexport async function getErc20Balance(\n config: Config,\n asset: Address,\n owner: Address,\n params?: ReadParams,\n) {\n return await readContract(config, {\n ...params,\n functionName: 'balanceOf',\n address: asset,\n args: [owner],\n abi: [\n {\n constant: true,\n inputs: [\n {\n name: '_owner',\n type: 'address',\n },\n ],\n name: 'balanceOf',\n outputs: [\n {\n name: 'balance',\n type: 'uint256',\n },\n ],\n stateMutability: 'view',\n type: 'function',\n },\n ],\n });\n}\n"],"names":["RegistryType","CheatCodes","TRANSFER_SIGNATURE","events","bytes4","input","slice","isHex","keccak256","toHex","getDeployedContractAddress","config","hash","waitParams","receipt","waitForTransactionReceipt","NoContractAddressUponReceiptError","awaitResult","hashPromise","result","assertValidAddressByChainId","addressByChainId","desiredChainId","chainId","wagmiChainId","getChainId","wagmiAccount","getAccount","InvalidProtocolChainIdError","getErc20Balance","asset","owner","params","readContract"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -11,10 +11,10 @@ import { match } from 'ts-pattern';
|
|
|
11
11
|
import {
|
|
12
12
|
type AbiEvent,
|
|
13
13
|
type AbiFunction,
|
|
14
|
-
AbiItem,
|
|
15
14
|
type Address,
|
|
16
15
|
type GetLogsReturnType,
|
|
17
16
|
type GetTransactionParameters,
|
|
17
|
+
type GetTransactionReceiptReturnType,
|
|
18
18
|
type Hex,
|
|
19
19
|
type Log,
|
|
20
20
|
type Transaction,
|
|
@@ -35,6 +35,7 @@ import type {
|
|
|
35
35
|
} from '../Deployable/Deployable';
|
|
36
36
|
import { DeployableTarget } from '../Deployable/DeployableTarget';
|
|
37
37
|
import {
|
|
38
|
+
DecodedArgsError,
|
|
38
39
|
DecodedArgsMalformedError,
|
|
39
40
|
FieldValueNotComparableError,
|
|
40
41
|
FieldValueUndefinedError,
|
|
@@ -51,6 +52,7 @@ import {
|
|
|
51
52
|
type Overwrite,
|
|
52
53
|
type ReadParams,
|
|
53
54
|
RegistryType,
|
|
55
|
+
TRANSFER_SIGNATURE,
|
|
54
56
|
type WriteParams,
|
|
55
57
|
} from '../utils';
|
|
56
58
|
|
|
@@ -714,6 +716,12 @@ export class EventAction extends DeployableTarget<
|
|
|
714
716
|
) {
|
|
715
717
|
return false;
|
|
716
718
|
}
|
|
719
|
+
|
|
720
|
+
// Special handling for Transfer events
|
|
721
|
+
if (actionStep.signature === TRANSFER_SIGNATURE) {
|
|
722
|
+
return this.decodeTransferLogs(receipt, actionStep);
|
|
723
|
+
}
|
|
724
|
+
|
|
717
725
|
const decodedLogs = receipt.logs
|
|
718
726
|
.filter((log) => log.topics[0] === toEventSelector(event))
|
|
719
727
|
.map((log) => {
|
|
@@ -767,6 +775,82 @@ export class EventAction extends DeployableTarget<
|
|
|
767
775
|
return false;
|
|
768
776
|
}
|
|
769
777
|
|
|
778
|
+
/**
|
|
779
|
+
* Decodes transfer logs specifically for ERC721 and ERC20 Transfer events.
|
|
780
|
+
*
|
|
781
|
+
* This special handling is required because both ERC20 and ERC721 Transfer events:
|
|
782
|
+
* 1. Share the same event signature (0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef)
|
|
783
|
+
* 2. Have similar but distinct structures:
|
|
784
|
+
* - ERC721: Transfer(address indexed from, address indexed to, uint256 indexed tokenId)
|
|
785
|
+
* - ERC20: Transfer(address indexed from, address indexed to, uint256 value)
|
|
786
|
+
*
|
|
787
|
+
* This causes signature collisions in the known signatures package, requiring us to
|
|
788
|
+
* try decoding both ways to determine which type of Transfer event we're dealing with.
|
|
789
|
+
*
|
|
790
|
+
* @param {GetTransactionReceiptReturnType} receipt - The transaction receipt containing the logs
|
|
791
|
+
* @param {ActionStep} actionStep - The action step being validated
|
|
792
|
+
* @returns {Promise<boolean>} - Returns true if the transfer logs are valid for either ERC20 or ERC721
|
|
793
|
+
* @throws {DecodedArgsError} - Throws if neither ERC20 nor ERC721 decoding succeeds
|
|
794
|
+
*/
|
|
795
|
+
private async decodeTransferLogs(
|
|
796
|
+
receipt: GetTransactionReceiptReturnType,
|
|
797
|
+
actionStep: ActionStep,
|
|
798
|
+
) {
|
|
799
|
+
const filteredLogs = receipt.logs.filter(
|
|
800
|
+
(log) => log.topics[0] === TRANSFER_SIGNATURE,
|
|
801
|
+
);
|
|
802
|
+
|
|
803
|
+
// ERC721
|
|
804
|
+
try {
|
|
805
|
+
const decodedLogs = filteredLogs.map((log) => {
|
|
806
|
+
const { eventName, args } = decodeEventLog({
|
|
807
|
+
abi: [
|
|
808
|
+
{
|
|
809
|
+
name: 'Transfer',
|
|
810
|
+
type: 'event',
|
|
811
|
+
inputs: [
|
|
812
|
+
{ type: 'address', indexed: true },
|
|
813
|
+
{ type: 'address', indexed: true },
|
|
814
|
+
{ type: 'uint256', indexed: true },
|
|
815
|
+
],
|
|
816
|
+
},
|
|
817
|
+
],
|
|
818
|
+
data: log.data,
|
|
819
|
+
topics: log.topics,
|
|
820
|
+
});
|
|
821
|
+
return { ...log, eventName, args };
|
|
822
|
+
});
|
|
823
|
+
|
|
824
|
+
return this.isActionEventValid(actionStep, decodedLogs);
|
|
825
|
+
} catch {
|
|
826
|
+
// ERC20
|
|
827
|
+
try {
|
|
828
|
+
const decodedLogs = filteredLogs.map((log) => {
|
|
829
|
+
const { eventName, args } = decodeEventLog({
|
|
830
|
+
abi: [
|
|
831
|
+
{
|
|
832
|
+
name: 'Transfer',
|
|
833
|
+
type: 'event',
|
|
834
|
+
inputs: [
|
|
835
|
+
{ type: 'address', indexed: true },
|
|
836
|
+
{ type: 'address', indexed: true },
|
|
837
|
+
{ type: 'uint256' },
|
|
838
|
+
],
|
|
839
|
+
},
|
|
840
|
+
],
|
|
841
|
+
data: log.data,
|
|
842
|
+
topics: log.topics,
|
|
843
|
+
});
|
|
844
|
+
return { ...log, eventName, args };
|
|
845
|
+
});
|
|
846
|
+
|
|
847
|
+
return this.isActionEventValid(actionStep, decodedLogs);
|
|
848
|
+
} catch {
|
|
849
|
+
throw new DecodedArgsError('Failed to decode transfer logs');
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
|
|
770
854
|
/**
|
|
771
855
|
* Validates a single action function with a given criteria against the transaction input.
|
|
772
856
|
*
|
package/src/BoostCore.ts
CHANGED
|
@@ -86,6 +86,10 @@ import {
|
|
|
86
86
|
ERC20PeggedIncentive,
|
|
87
87
|
type ERC20PeggedIncentivePayload,
|
|
88
88
|
} from './Incentives/ERC20PeggedIncentive';
|
|
89
|
+
import {
|
|
90
|
+
ERC20PeggedVariableCriteriaIncentive,
|
|
91
|
+
type ERC20PeggedVariableCriteriaIncentivePayload,
|
|
92
|
+
} from './Incentives/ERC20PeggedVariableCriteriaIncentive';
|
|
89
93
|
import {
|
|
90
94
|
ERC20VariableCriteriaIncentive,
|
|
91
95
|
type ERC20VariableCriteriaIncentivePayload,
|
|
@@ -1498,6 +1502,29 @@ export class BoostCore extends Deployable<
|
|
|
1498
1502
|
);
|
|
1499
1503
|
}
|
|
1500
1504
|
|
|
1505
|
+
/**
|
|
1506
|
+
* Bound {@link ERC20PeggedVariableCriteriaIncentive} constructor that reuses the same configuration as the Boost Core instance.
|
|
1507
|
+
*
|
|
1508
|
+
* @example
|
|
1509
|
+
* ```ts
|
|
1510
|
+
* const validator = core.ERC20PeggedVariableCriteriaIncentive({ ... }) // is roughly equivalent to
|
|
1511
|
+
* const validator = new ERC20PeggedVariableCriteriaIncentive({ config: core._config, account: core._account }, { ... })
|
|
1512
|
+
* ```
|
|
1513
|
+
* @param {DeployablePayloadOrAddress<ERC20PeggedVariableCriteriaIncentive>} options
|
|
1514
|
+
* @param {?boolean} [isBase]
|
|
1515
|
+
* @returns {ERC20PeggedVariableCriteriaIncentive}
|
|
1516
|
+
* */
|
|
1517
|
+
ERC20PeggedVariableCriteriaIncentive(
|
|
1518
|
+
options: DeployablePayloadOrAddress<ERC20PeggedVariableCriteriaIncentivePayload>,
|
|
1519
|
+
isBase?: boolean,
|
|
1520
|
+
) {
|
|
1521
|
+
return new ERC20PeggedVariableCriteriaIncentive(
|
|
1522
|
+
{ config: this._config, account: this._account },
|
|
1523
|
+
options,
|
|
1524
|
+
isBase,
|
|
1525
|
+
);
|
|
1526
|
+
}
|
|
1527
|
+
|
|
1501
1528
|
/**
|
|
1502
1529
|
* Bound {@link ERC20VariableIncentive} constructor that reuses the same configuration as the Boost Core instance.
|
|
1503
1530
|
*
|
|
@@ -511,6 +511,7 @@ export function prepareCGDAIncentivePayload({
|
|
|
511
511
|
rewardDecay,
|
|
512
512
|
rewardBoost,
|
|
513
513
|
totalBudget,
|
|
514
|
+
manager,
|
|
514
515
|
}: CGDAIncentivePayload) {
|
|
515
516
|
return encodeAbiParameters(
|
|
516
517
|
[
|
|
@@ -519,7 +520,8 @@ export function prepareCGDAIncentivePayload({
|
|
|
519
520
|
{ type: 'uint256', name: 'rewardDecay' },
|
|
520
521
|
{ type: 'uint256', name: 'rewardBoost' },
|
|
521
522
|
{ type: 'uint256', name: 'totalBudget' },
|
|
523
|
+
{ type: 'address', name: 'manager' },
|
|
522
524
|
],
|
|
523
|
-
[asset, initialReward, rewardDecay, rewardBoost, totalBudget],
|
|
525
|
+
[asset, initialReward, rewardDecay, rewardBoost, totalBudget, manager],
|
|
524
526
|
);
|
|
525
527
|
}
|
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
import { selectors as eventSelectors } from "@boostxyz/signatures/events";
|
|
2
|
+
import { selectors as funcSelectors } from "@boostxyz/signatures/functions";
|
|
3
|
+
import type { MockERC20 } from "@boostxyz/test/MockERC20";
|
|
4
|
+
import type { MockERC721 } from "@boostxyz/test/MockERC721";
|
|
5
|
+
import { accounts } from "@boostxyz/test/accounts";
|
|
6
|
+
import {
|
|
7
|
+
type BudgetFixtures,
|
|
8
|
+
type Fixtures,
|
|
9
|
+
defaultOptions,
|
|
10
|
+
deployFixtures,
|
|
11
|
+
freshBoost,
|
|
12
|
+
fundBudget,
|
|
13
|
+
fundErc20,
|
|
14
|
+
fundErc721,
|
|
15
|
+
} from "@boostxyz/test/helpers";
|
|
16
|
+
import { loadFixture } from "@nomicfoundation/hardhat-network-helpers";
|
|
17
|
+
import {
|
|
18
|
+
type Hex,
|
|
19
|
+
isAddress,
|
|
20
|
+
isAddressEqual,
|
|
21
|
+
parseEther,
|
|
22
|
+
zeroAddress,
|
|
23
|
+
zeroHash,
|
|
24
|
+
} from "viem";
|
|
25
|
+
import { beforeAll, beforeEach, describe, expect, test } from "vitest";
|
|
26
|
+
import { SignatureType } from "../Actions/EventAction";
|
|
27
|
+
import type { Boost } from "../Boost";
|
|
28
|
+
import {
|
|
29
|
+
type ERC20PeggedVariableCriteriaIncentive,
|
|
30
|
+
} from "./ERC20PeggedVariableCriteriaIncentive";
|
|
31
|
+
import {
|
|
32
|
+
type IncentiveCriteria,
|
|
33
|
+
gasRebateIncentiveCriteria,
|
|
34
|
+
} from "./ERC20VariableCriteriaIncentive";
|
|
35
|
+
import { allKnownSignatures } from "@boostxyz/test/allKnownSignatures";
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* A basic ERC721 mint scalar criteria for testing
|
|
39
|
+
*
|
|
40
|
+
* @param {MockERC721} erc721 - The ERC721 contract
|
|
41
|
+
* @returns {IncentiveCriteria} - Returns a basic incentive criteria
|
|
42
|
+
*/
|
|
43
|
+
export function basicErc721TransferScalarCriteria(
|
|
44
|
+
erc721: MockERC721,
|
|
45
|
+
): IncentiveCriteria {
|
|
46
|
+
return {
|
|
47
|
+
criteriaType: SignatureType.FUNC,
|
|
48
|
+
signature: funcSelectors["transferFrom(address,address,uint256)"] as Hex, // Function selector for mint
|
|
49
|
+
fieldIndex: 2, // Field where the scalar value resides
|
|
50
|
+
targetContract: erc721.assertValidAddress(),
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* A basic ERC721 mint scalar criteria for testing
|
|
56
|
+
*
|
|
57
|
+
* @param {MockERC721} erc721 - The ERC721 contract
|
|
58
|
+
* @returns {IncentiveCriteria} - Returns a basic incentive criteria
|
|
59
|
+
*/
|
|
60
|
+
export function basicErc721MintScalarCriteria(
|
|
61
|
+
erc721: MockERC721,
|
|
62
|
+
): IncentiveCriteria {
|
|
63
|
+
return {
|
|
64
|
+
criteriaType: SignatureType.EVENT,
|
|
65
|
+
signature: eventSelectors[
|
|
66
|
+
"Transfer(address indexed,address indexed,uint256 indexed)"
|
|
67
|
+
] as Hex, // Function selector for mint
|
|
68
|
+
fieldIndex: 2, // Field where the scalar value resides
|
|
69
|
+
targetContract: erc721.assertValidAddress(),
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
let fixtures: Fixtures,
|
|
74
|
+
erc20: MockERC20,
|
|
75
|
+
erc721: MockERC721,
|
|
76
|
+
erc20Incentive: ERC20PeggedVariableCriteriaIncentive,
|
|
77
|
+
budgets: BudgetFixtures,
|
|
78
|
+
boost: Boost;
|
|
79
|
+
|
|
80
|
+
describe("ERC20VariableCriteriaIncentive", () => {
|
|
81
|
+
beforeAll(async () => {
|
|
82
|
+
fixtures = await loadFixture(deployFixtures(defaultOptions));
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
beforeEach(async () => {
|
|
86
|
+
budgets = await loadFixture(fundBudget(defaultOptions, fixtures));
|
|
87
|
+
erc20 = await loadFixture(fundErc20(defaultOptions));
|
|
88
|
+
erc721 = await loadFixture(fundErc721(defaultOptions));
|
|
89
|
+
erc20Incentive = fixtures.core.ERC20PeggedVariableCriteriaIncentive({
|
|
90
|
+
asset: budgets.erc20.assertValidAddress(),
|
|
91
|
+
reward: parseEther("1"),
|
|
92
|
+
limit: parseEther("10"),
|
|
93
|
+
maxReward: parseEther("20"),
|
|
94
|
+
criteria: basicErc721TransferScalarCriteria(erc721),
|
|
95
|
+
peg: erc20.assertValidAddress(),
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
boost = await freshBoost(fixtures, {
|
|
99
|
+
budget: budgets.budget,
|
|
100
|
+
incentives: [erc20Incentive],
|
|
101
|
+
});
|
|
102
|
+
expect(isAddress(boost.incentives[0]!.assertValidAddress())).toBe(true);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
describe("Basic Parameters", () => {
|
|
106
|
+
test("should return correct asset address", async () => {
|
|
107
|
+
const asset = await erc20Incentive.asset();
|
|
108
|
+
expect(isAddressEqual(asset, budgets.erc20.assertValidAddress())).toBe(true);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
test("should return correct peg token address", async () => {
|
|
112
|
+
const peg = await erc20Incentive.peg();
|
|
113
|
+
expect(isAddressEqual(peg, erc20.assertValidAddress())).toBe(true);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
test("should return correct reward amount", async () => {
|
|
117
|
+
const reward = await erc20Incentive.reward();
|
|
118
|
+
expect(reward).toBe(parseEther("1"));
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
test("should return correct limit", async () => {
|
|
122
|
+
const limit = await erc20Incentive.limit();
|
|
123
|
+
expect(limit).toBe(parseEther("10"));
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
test("should return correct max reward", async () => {
|
|
127
|
+
const maxReward = await erc20Incentive.getMaxReward();
|
|
128
|
+
expect(maxReward).toBe(parseEther("20"));
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
describe("Remaining Claims", () => {
|
|
133
|
+
test("should calculate remaining claim potential correctly", async () => {
|
|
134
|
+
const remaining = await erc20Incentive.getRemainingClaimPotential();
|
|
135
|
+
const limit = await erc20Incentive.limit();
|
|
136
|
+
const totalClaimed = await erc20Incentive.totalClaimed();
|
|
137
|
+
expect(remaining).toBe(limit - totalClaimed);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
test("should return true for canBeClaimed when claims remain", async () => {
|
|
141
|
+
const canBeClaimed = await erc20Incentive.canBeClaimed();
|
|
142
|
+
expect(canBeClaimed).toBe(true);
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
describe("getIncentiveCriteria", () => {
|
|
147
|
+
test("should fetch incentive criteria successfully", async () => {
|
|
148
|
+
const incentive = boost.incentives[0] as ERC20PeggedVariableCriteriaIncentive;
|
|
149
|
+
const criteria = await incentive.getIncentiveCriteria();
|
|
150
|
+
expect(criteria).toMatchObject({
|
|
151
|
+
criteriaType: SignatureType.FUNC,
|
|
152
|
+
signature: expect.any(String),
|
|
153
|
+
fieldIndex: expect.any(Number),
|
|
154
|
+
targetContract: expect.any(String),
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
describe("Claim Data", () => {
|
|
160
|
+
test("should properly encode claim data", () => {
|
|
161
|
+
const rewardAmount = parseEther("1");
|
|
162
|
+
const encodedData = erc20Incentive.buildClaimData(rewardAmount);
|
|
163
|
+
expect(encodedData).toBe(
|
|
164
|
+
"0x0000000000000000000000000000000000000000000000000de0b6b3a7640000"
|
|
165
|
+
);
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
describe("Owner", () => {
|
|
170
|
+
test("should return correct owner", async () => {
|
|
171
|
+
const owner = await erc20Incentive.owner();
|
|
172
|
+
expect(isAddressEqual(owner, fixtures.core.assertValidAddress())).toBe(true);
|
|
173
|
+
});
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
describe("Current Reward", () => {
|
|
177
|
+
test("should return valid current reward", async () => {
|
|
178
|
+
const currentReward = await erc20Incentive.currentReward();
|
|
179
|
+
expect(currentReward).toBeDefined();
|
|
180
|
+
expect(currentReward).toBeTypeOf("bigint");
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
describe("getIncentiveScalar", () => {
|
|
185
|
+
test("should return a valid scalar for function-based criteria", async () => {
|
|
186
|
+
const recipient = accounts[1].account;
|
|
187
|
+
|
|
188
|
+
const { hash } = await erc721.transferFromRaw(
|
|
189
|
+
accounts[0].account,
|
|
190
|
+
recipient,
|
|
191
|
+
1n,
|
|
192
|
+
);
|
|
193
|
+
const scalar = await erc20Incentive.getIncentiveScalar({
|
|
194
|
+
hash,
|
|
195
|
+
chainId: 31337,
|
|
196
|
+
knownSignatures: allKnownSignatures,
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
expect(scalar).toBe(1n);
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
test("should return a valid scalar for event-based criteria", async () => {
|
|
203
|
+
boost = await freshBoost(fixtures, {
|
|
204
|
+
budget: budgets.budget,
|
|
205
|
+
incentives: [erc20Incentive],
|
|
206
|
+
});
|
|
207
|
+
const recipient = accounts[1].account;
|
|
208
|
+
const { hash } = await erc721.transferFromRaw(
|
|
209
|
+
accounts[0].account,
|
|
210
|
+
recipient,
|
|
211
|
+
1n,
|
|
212
|
+
);
|
|
213
|
+
const scalar = await erc20Incentive.getIncentiveScalar({
|
|
214
|
+
hash,
|
|
215
|
+
chainId: 31337,
|
|
216
|
+
knownSignatures: allKnownSignatures,
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
expect(scalar).toBe(1n);
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
test("gasRebateIncentiveCriteria generates correct incentive criteria", async () => {
|
|
223
|
+
// Ensure that the gasRebateIncentiveCriteria returns the correct structure
|
|
224
|
+
const gasRebateCriteria = gasRebateIncentiveCriteria();
|
|
225
|
+
|
|
226
|
+
erc20Incentive = fixtures.core.ERC20PeggedVariableCriteriaIncentive({
|
|
227
|
+
asset: budgets.erc20.assertValidAddress(),
|
|
228
|
+
reward: 1n,
|
|
229
|
+
limit: 1n,
|
|
230
|
+
maxReward: 1n,
|
|
231
|
+
criteria: gasRebateCriteria,
|
|
232
|
+
peg: zeroAddress,
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
// Validate the returned structure against the expected criteria values
|
|
236
|
+
expect(gasRebateCriteria).toEqual({
|
|
237
|
+
criteriaType: SignatureType.EVENT,
|
|
238
|
+
signature: zeroHash,
|
|
239
|
+
fieldIndex: 255,
|
|
240
|
+
targetContract: zeroAddress,
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
boost = await freshBoost(fixtures, {
|
|
244
|
+
budget: budgets.budget,
|
|
245
|
+
incentives: [erc20Incentive],
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
// Validate that the deployed incentive has the correct criteria set up
|
|
249
|
+
const deployedIncentive = (await boost
|
|
250
|
+
.incentives[0]) as ERC20PeggedVariableCriteriaIncentive;
|
|
251
|
+
const deployedCriteria = await deployedIncentive.getIncentiveCriteria();
|
|
252
|
+
expect(deployedCriteria.criteriaType).toBe(SignatureType.EVENT);
|
|
253
|
+
expect(deployedCriteria.signature).toBe(zeroHash);
|
|
254
|
+
expect(deployedCriteria.fieldIndex).toBe(255);
|
|
255
|
+
expect(deployedCriteria.targetContract).toBe(zeroAddress);
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
test("should throw NoMatchingLogsError for event criteria with no matching logs", async () => {
|
|
259
|
+
const recipient = accounts[1].account;
|
|
260
|
+
|
|
261
|
+
const { hash } = await erc20.mintRaw(recipient, parseEther("100"));
|
|
262
|
+
|
|
263
|
+
try {
|
|
264
|
+
await erc20Incentive.getIncentiveScalar({
|
|
265
|
+
hash,
|
|
266
|
+
chainId: 31337,
|
|
267
|
+
knownSignatures: allKnownSignatures,
|
|
268
|
+
});
|
|
269
|
+
} catch (e) {
|
|
270
|
+
expect((e as Error).name).toBe("DecodedArgsError");
|
|
271
|
+
}
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
test("should throw DecodedArgsError for invalid function-based data", async () => {
|
|
275
|
+
const recipient = accounts[1].account;
|
|
276
|
+
const { hash } = await erc20.mintRaw(recipient, parseEther("100"));
|
|
277
|
+
|
|
278
|
+
try {
|
|
279
|
+
await erc20Incentive.getIncentiveScalar({
|
|
280
|
+
hash,
|
|
281
|
+
chainId: 31337,
|
|
282
|
+
knownSignatures: allKnownSignatures,
|
|
283
|
+
});
|
|
284
|
+
} catch (e) {
|
|
285
|
+
expect((e as Error).name).toBe("DecodedArgsError");
|
|
286
|
+
}
|
|
287
|
+
});
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
test("can properly encode a uint256", () => {
|
|
291
|
+
//@ts-ignore
|
|
292
|
+
const incentive = fixtures.core.ERC20VariableCriteriaIncentive();
|
|
293
|
+
expect(incentive.buildClawbackData(1n)).toBe(
|
|
294
|
+
"0x0000000000000000000000000000000000000000000000000000000000000001",
|
|
295
|
+
);
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
test("can clawback via a budget", async () => {
|
|
299
|
+
// rebase this
|
|
300
|
+
const boost = await freshBoost(fixtures, {
|
|
301
|
+
budget: budgets.budget,
|
|
302
|
+
incentives: [erc20Incentive],
|
|
303
|
+
});
|
|
304
|
+
const [amount, address] = await budgets.budget.clawbackFromTarget(
|
|
305
|
+
fixtures.core.assertValidAddress(),
|
|
306
|
+
erc20Incentive.buildClawbackData(1n),
|
|
307
|
+
boost.id,
|
|
308
|
+
0,
|
|
309
|
+
);
|
|
310
|
+
expect(amount).toBe(1n);
|
|
311
|
+
expect(isAddressEqual(address, budgets.erc20.assertValidAddress())).toBe(
|
|
312
|
+
true,
|
|
313
|
+
);
|
|
314
|
+
});
|
|
315
|
+
});
|