@boostxyz/sdk 1.1.0-alpha.23 → 2.0.0-alpha.25
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 +7 -7
- package/dist/Actions/EventAction.cjs +1 -1
- package/dist/Actions/EventAction.cjs.map +1 -1
- package/dist/Actions/EventAction.d.ts +32 -5
- package/dist/Actions/EventAction.d.ts.map +1 -1
- package/dist/Actions/EventAction.js +164 -137
- package/dist/Actions/EventAction.js.map +1 -1
- package/dist/AllowLists/AllowList.cjs +1 -1
- package/dist/AllowLists/AllowList.js +10 -10
- package/dist/AllowLists/SimpleAllowList.cjs +1 -1
- package/dist/AllowLists/SimpleAllowList.js +10 -10
- 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 -1
- package/dist/BoostCore.cjs.map +1 -1
- package/dist/BoostCore.d.ts +564 -1
- package/dist/BoostCore.d.ts.map +1 -1
- package/dist/BoostCore.js +1322 -29
- package/dist/BoostCore.js.map +1 -1
- package/dist/BoostRegistry.cjs +1 -1
- package/dist/BoostRegistry.js +27 -27
- 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 +23 -23
- 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 +23 -23
- package/dist/Incentives/AllowListIncentive.cjs +1 -1
- package/dist/Incentives/AllowListIncentive.js +8 -8
- package/dist/Incentives/CGDAIncentive.cjs +1 -1
- package/dist/Incentives/CGDAIncentive.js +12 -12
- package/dist/Incentives/ERC20Incentive.cjs +1 -1
- package/dist/Incentives/ERC20Incentive.js +16 -16
- package/dist/Incentives/ERC20VariableCriteriaIncentive.cjs +2 -0
- package/dist/Incentives/ERC20VariableCriteriaIncentive.cjs.map +1 -0
- package/dist/Incentives/ERC20VariableCriteriaIncentive.d.ts +42 -15
- package/dist/Incentives/ERC20VariableCriteriaIncentive.d.ts.map +1 -1
- package/dist/Incentives/ERC20VariableCriteriaIncentive.js +188 -0
- package/dist/Incentives/ERC20VariableCriteriaIncentive.js.map +1 -0
- package/dist/Incentives/ERC20VariableIncentive.cjs +2 -0
- package/dist/Incentives/ERC20VariableIncentive.cjs.map +1 -0
- package/dist/Incentives/ERC20VariableIncentive.d.ts +7 -5
- package/dist/Incentives/ERC20VariableIncentive.d.ts.map +1 -1
- package/dist/{Incentive-rM5nKznp.js → Incentives/ERC20VariableIncentive.js} +42 -67
- package/dist/Incentives/ERC20VariableIncentive.js.map +1 -0
- package/dist/Incentives/Incentive.cjs +1 -1
- package/dist/Incentives/Incentive.cjs.map +1 -1
- package/dist/Incentives/Incentive.d.ts +552 -3
- package/dist/Incentives/Incentive.d.ts.map +1 -1
- package/dist/Incentives/Incentive.js +40 -16
- package/dist/Incentives/Incentive.js.map +1 -1
- package/dist/Incentives/PointsIncentive.cjs +1 -1
- package/dist/Incentives/PointsIncentive.js +8 -8
- package/dist/{SimpleDenyList-DNj5qDWM.cjs → SimpleDenyList-BdlpZdDz.cjs} +2 -2
- package/dist/{SimpleDenyList-DNj5qDWM.cjs.map → SimpleDenyList-BdlpZdDz.cjs.map} +1 -1
- package/dist/{SimpleDenyList-BDXpY74P.js → SimpleDenyList-D0cyVUVv.js} +16 -16
- package/dist/{SimpleDenyList-BDXpY74P.js.map → SimpleDenyList-D0cyVUVv.js.map} +1 -1
- package/dist/Validators/LimitedSignerValidator.cjs +2 -0
- package/dist/Validators/LimitedSignerValidator.cjs.map +1 -0
- package/dist/Validators/LimitedSignerValidator.d.ts +806 -0
- package/dist/Validators/LimitedSignerValidator.d.ts.map +1 -0
- package/dist/Validators/LimitedSignerValidator.js +299 -0
- package/dist/Validators/LimitedSignerValidator.js.map +1 -0
- package/dist/Validators/SignerValidator.cjs +1 -1
- package/dist/Validators/SignerValidator.cjs.map +1 -1
- package/dist/Validators/SignerValidator.d.ts +2 -2
- package/dist/Validators/SignerValidator.js +5 -5
- package/dist/Validators/SignerValidator.js.map +1 -1
- package/dist/Validators/Validator.cjs +1 -1
- package/dist/Validators/Validator.cjs.map +1 -1
- package/dist/Validators/Validator.d.ts +22 -2
- package/dist/Validators/Validator.d.ts.map +1 -1
- package/dist/Validators/Validator.js +26 -17
- package/dist/Validators/Validator.js.map +1 -1
- package/dist/{componentInterfaces-BBCFkrZv.js → componentInterfaces-bIVo8eyA.js} +9 -9
- package/dist/componentInterfaces-bIVo8eyA.js.map +1 -0
- package/dist/{deployments-fJsWblwS.js → deployments-Ccx1MtoK.js} +5 -5
- package/dist/deployments-Ccx1MtoK.js.map +1 -0
- package/dist/{generated-CsNyWPKA.js → generated-BAKNSOjJ.js} +661 -381
- package/dist/generated-BAKNSOjJ.js.map +1 -0
- package/dist/generated-Cqn4wS01.cjs +3 -0
- package/dist/generated-Cqn4wS01.cjs.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 +145 -132
- package/dist/index.js.map +1 -1
- package/dist/utils.cjs +1 -1
- package/dist/utils.cjs.map +1 -1
- package/dist/utils.d.ts +10 -0
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +27 -26
- package/dist/utils.js.map +1 -1
- package/package.json +21 -5
- package/src/Actions/EventAction.test.ts +381 -122
- package/src/Actions/EventAction.ts +85 -23
- package/src/BoostCore.test.ts +3 -3
- package/src/BoostCore.ts +51 -5
- package/src/Incentives/AllowListIncentive.test.ts +19 -21
- package/src/Incentives/CGDAIncentive.test.ts +20 -22
- package/src/Incentives/ERC1155Incentive.test.ts +14 -15
- package/src/Incentives/ERC20Incentive.test.ts +17 -19
- package/src/Incentives/ERC20VariableCriteriaIncentive.test.ts +62 -10
- package/src/Incentives/ERC20VariableCriteriaIncentive.ts +79 -37
- package/src/Incentives/ERC20VariableIncentive.test.ts +16 -18
- package/src/Incentives/ERC20VariableIncentive.ts +16 -8
- package/src/Incentives/Incentive.ts +4 -1
- package/src/Incentives/PointsIncentive.test.ts +3 -5
- package/src/Validators/LimitedSignerValidator.test.ts +223 -0
- package/src/Validators/LimitedSignerValidator.ts +707 -0
- package/src/Validators/SignerValidator.test.ts +21 -22
- package/src/Validators/SignerValidator.ts +2 -2
- package/src/Validators/Validator.test.ts +7 -1
- package/src/Validators/Validator.ts +30 -3
- package/src/index.test.ts +2 -0
- package/src/index.ts +1 -0
- package/src/utils.ts +11 -0
- package/dist/BoostCore-Btl5BdAs.cjs +0 -3
- package/dist/BoostCore-Btl5BdAs.cjs.map +0 -1
- package/dist/BoostCore-CD56zbYX.js +0 -2641
- package/dist/BoostCore-CD56zbYX.js.map +0 -1
- package/dist/Incentive-CcnOIc8L.cjs +0 -2
- package/dist/Incentive-CcnOIc8L.cjs.map +0 -1
- package/dist/Incentive-rM5nKznp.js.map +0 -1
- package/dist/componentInterfaces-BBCFkrZv.js.map +0 -1
- package/dist/deployments-fJsWblwS.js.map +0 -1
- package/dist/generated-CsNyWPKA.js.map +0 -1
- package/dist/generated-DHerxf1y.cjs +0 -3
- package/dist/generated-DHerxf1y.cjs.map +0 -1
|
@@ -25,6 +25,7 @@ import {
|
|
|
25
25
|
fromHex,
|
|
26
26
|
isAddress,
|
|
27
27
|
isAddressEqual,
|
|
28
|
+
zeroAddress,
|
|
28
29
|
zeroHash,
|
|
29
30
|
} from 'viem';
|
|
30
31
|
import { EventAction as EventActionBases } from '../../dist/deployments.json';
|
|
@@ -46,6 +47,7 @@ import {
|
|
|
46
47
|
ValidationAbiMissingError,
|
|
47
48
|
} from '../errors';
|
|
48
49
|
import {
|
|
50
|
+
CheatCodes,
|
|
49
51
|
type Overwrite,
|
|
50
52
|
type ReadParams,
|
|
51
53
|
RegistryType,
|
|
@@ -221,12 +223,13 @@ export interface ActionStep {
|
|
|
221
223
|
* @property {AbiEvent | AbiFunction} [abiItem] - Optional ABI item definition.
|
|
222
224
|
* @property {EventLogs} [logs] - Event logs to validate against. Required if 'hash' is not provided.
|
|
223
225
|
* @property {Hex} [hash] - Transaction hash to validate against. Required if 'logs' is not provided.
|
|
224
|
-
* @property {number} [chainId] - Chain ID for the transaction.
|
|
226
|
+
* @property {number} [chainId] - Chain ID for the transaction.
|
|
225
227
|
*/
|
|
226
228
|
export type ValidateActionStepParams = {
|
|
227
229
|
knownSignatures: Record<Hex, AbiEvent | AbiFunction>;
|
|
228
230
|
abiItem?: AbiEvent | AbiFunction;
|
|
229
|
-
|
|
231
|
+
chainId: number;
|
|
232
|
+
} & ({ logs: EventLogs } | { hash: Hex });
|
|
230
233
|
|
|
231
234
|
/**
|
|
232
235
|
* You can either supply a simplified version of the payload, or one that explicitly declares action steps.
|
|
@@ -482,6 +485,8 @@ export class EventAction extends DeployableTarget<
|
|
|
482
485
|
/**
|
|
483
486
|
* Derives the action claimant address from a transaction based on the provided ActionClaimant configuration.
|
|
484
487
|
* This method supports both event-based and function-based claimant derivation.
|
|
488
|
+
* **Important**: The claimant is considered to be `transaction.from` when `claimant.fieldIndex` is 255 using CheatCodes enum.
|
|
489
|
+
* This may have unintended side effects for bridged transactions and SCW transactions, so these are considered unsupported use cases for the time being.
|
|
485
490
|
*
|
|
486
491
|
** @example
|
|
487
492
|
* // Example usage
|
|
@@ -515,7 +520,32 @@ export class EventAction extends DeployableTarget<
|
|
|
515
520
|
claimant: ActionClaimant,
|
|
516
521
|
params: ValidateActionStepParams,
|
|
517
522
|
): Promise<Address | undefined> {
|
|
523
|
+
// find message sender and return it
|
|
524
|
+
// WARNING: this is error prone in bridged transactions and SCW transactions, as this will return exit node
|
|
525
|
+
if (claimant.fieldIndex === CheatCodes.TX_SENDER_CLAIMANT) {
|
|
526
|
+
if ('hash' in params) {
|
|
527
|
+
const transaction = await getTransaction(this._config, {
|
|
528
|
+
hash: params.hash,
|
|
529
|
+
chainId: params.chainId,
|
|
530
|
+
});
|
|
531
|
+
return transaction.from;
|
|
532
|
+
}
|
|
533
|
+
if ('logs' in params) {
|
|
534
|
+
for (let log of params.logs) {
|
|
535
|
+
if (log.transactionHash) {
|
|
536
|
+
const transaction = await getTransaction(this._config, {
|
|
537
|
+
hash: log.transactionHash,
|
|
538
|
+
chainId: claimant.chainid,
|
|
539
|
+
});
|
|
540
|
+
return transaction.from;
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
return undefined;
|
|
545
|
+
}
|
|
546
|
+
|
|
518
547
|
const signature = claimant.signature;
|
|
548
|
+
|
|
519
549
|
if (claimant.signatureType === SignatureType.EVENT) {
|
|
520
550
|
let event: AbiEvent;
|
|
521
551
|
if (params.abiItem) event = params.abiItem as AbiEvent;
|
|
@@ -556,13 +586,10 @@ export class EventAction extends DeployableTarget<
|
|
|
556
586
|
}
|
|
557
587
|
return address;
|
|
558
588
|
}
|
|
559
|
-
if (
|
|
560
|
-
claimant.signatureType === SignatureType.FUNC &&
|
|
561
|
-
'hash' in params &&
|
|
562
|
-
'chainId' in params
|
|
563
|
-
) {
|
|
589
|
+
if (claimant.signatureType === SignatureType.FUNC && 'hash' in params) {
|
|
564
590
|
const transaction = await getTransaction(this._config, {
|
|
565
591
|
hash: params.hash,
|
|
592
|
+
chainId: params.chainId,
|
|
566
593
|
});
|
|
567
594
|
if (!isAddressEqual(transaction.to!, claimant.targetContract)) return;
|
|
568
595
|
let func: AbiFunction;
|
|
@@ -672,11 +699,9 @@ export class EventAction extends DeployableTarget<
|
|
|
672
699
|
return this.isActionEventValid(actionStep, params.logs);
|
|
673
700
|
}
|
|
674
701
|
|
|
675
|
-
const
|
|
676
|
-
chainId: params.chainId,
|
|
677
|
-
}) as PublicClient;
|
|
678
|
-
const receipt = await client.getTransactionReceipt({
|
|
702
|
+
const receipt = await getTransactionReceipt(this._config, {
|
|
679
703
|
hash: params.hash,
|
|
704
|
+
chainId: params.chainId,
|
|
680
705
|
});
|
|
681
706
|
const decodedLogs = receipt.logs.map((log) => {
|
|
682
707
|
const { eventName, args } = decodeEventLog({
|
|
@@ -692,11 +717,9 @@ export class EventAction extends DeployableTarget<
|
|
|
692
717
|
}
|
|
693
718
|
if (actionStep.signatureType === SignatureType.FUNC) {
|
|
694
719
|
if ('hash' in params && 'chainId' in params) {
|
|
695
|
-
const
|
|
696
|
-
chainId: params.chainId,
|
|
697
|
-
}) as PublicClient;
|
|
698
|
-
const transaction = await client.getTransaction({
|
|
720
|
+
const transaction = await getTransaction(this._config, {
|
|
699
721
|
hash: params.hash,
|
|
722
|
+
chainId: params.chainId,
|
|
700
723
|
});
|
|
701
724
|
return this.isActionFunctionValid(actionStep, transaction, params);
|
|
702
725
|
}
|
|
@@ -802,7 +825,7 @@ export class EventAction extends DeployableTarget<
|
|
|
802
825
|
if (
|
|
803
826
|
criteria.filterType === FilterType.EQUAL &&
|
|
804
827
|
criteria.fieldType === PrimitiveType.BYTES &&
|
|
805
|
-
criteria.fieldIndex ===
|
|
828
|
+
criteria.fieldIndex === CheatCodes.ANY_ACTION_PARAM
|
|
806
829
|
) {
|
|
807
830
|
return true;
|
|
808
831
|
}
|
|
@@ -906,7 +929,7 @@ export class EventAction extends DeployableTarget<
|
|
|
906
929
|
|
|
907
930
|
/**
|
|
908
931
|
* Validates a {@link Log} against a given criteria.
|
|
909
|
-
* If the criteria's fieldIndex is 255, it is reserved for anyValidation
|
|
932
|
+
* If the criteria's fieldIndex is 255 (using CheatCodes enum), it is reserved for anyValidation
|
|
910
933
|
*
|
|
911
934
|
* @param {Criteria} criteria - The criteria to validate against.
|
|
912
935
|
* @param {Log} log - The Viem event log.
|
|
@@ -918,7 +941,8 @@ export class EventAction extends DeployableTarget<
|
|
|
918
941
|
): boolean {
|
|
919
942
|
if (
|
|
920
943
|
!Array.isArray(log.args) ||
|
|
921
|
-
(log.args.length <= criteria.fieldIndex &&
|
|
944
|
+
(log.args.length <= criteria.fieldIndex &&
|
|
945
|
+
criteria.fieldIndex !== CheatCodes.ANY_ACTION_PARAM)
|
|
922
946
|
) {
|
|
923
947
|
throw new DecodedArgsMalformedError({
|
|
924
948
|
log,
|
|
@@ -928,7 +952,9 @@ export class EventAction extends DeployableTarget<
|
|
|
928
952
|
}
|
|
929
953
|
|
|
930
954
|
const fieldValue =
|
|
931
|
-
criteria.fieldIndex ===
|
|
955
|
+
criteria.fieldIndex === CheatCodes.ANY_ACTION_PARAM
|
|
956
|
+
? zeroHash
|
|
957
|
+
: log.args.at(criteria.fieldIndex);
|
|
932
958
|
|
|
933
959
|
if (fieldValue === undefined) {
|
|
934
960
|
throw new FieldValueUndefinedError({ log, criteria, fieldValue });
|
|
@@ -938,7 +964,7 @@ export class EventAction extends DeployableTarget<
|
|
|
938
964
|
|
|
939
965
|
/**
|
|
940
966
|
* Validates a function's decoded arguments against a given criteria.
|
|
941
|
-
* If the criteria's fieldIndex is 255, it is reserved for anyValidation
|
|
967
|
+
* If the criteria's fieldIndex is 255 (using CheatCodes enum), it is reserved for anyValidation
|
|
942
968
|
*
|
|
943
969
|
* @param {Criteria} criteria - The criteria to validate against.
|
|
944
970
|
* @param {unknown[]} decodedArgs - The decoded arguments of the function call.
|
|
@@ -949,7 +975,9 @@ export class EventAction extends DeployableTarget<
|
|
|
949
975
|
decodedArgs: readonly (string | bigint)[],
|
|
950
976
|
): boolean {
|
|
951
977
|
const fieldValue =
|
|
952
|
-
criteria.fieldIndex ===
|
|
978
|
+
criteria.fieldIndex === CheatCodes.ANY_ACTION_PARAM
|
|
979
|
+
? zeroHash
|
|
980
|
+
: decodedArgs[criteria.fieldIndex];
|
|
953
981
|
if (fieldValue === undefined) {
|
|
954
982
|
throw new FieldValueUndefinedError({
|
|
955
983
|
criteria,
|
|
@@ -1221,7 +1249,7 @@ export function prepareEventActionPayload({
|
|
|
1221
1249
|
* This function returns a Criteria object with the following properties:
|
|
1222
1250
|
* - filterType: Set to EQUAL for exact matching
|
|
1223
1251
|
* - fieldType: Set to BYTES to handle any data type
|
|
1224
|
-
* - fieldIndex: Set to 255, which is typically used to indicate "any" or "all" in this context
|
|
1252
|
+
* - fieldIndex: Set to 255, which is typically used to indicate "any" or "all" in this context using CheatCodes enum
|
|
1225
1253
|
* - filterData: Set to zeroHash (0x0000...0000)
|
|
1226
1254
|
*
|
|
1227
1255
|
* @returns {Criteria} A Criteria object that can be used to match any action parameter
|
|
@@ -1238,7 +1266,41 @@ export function anyActionParameter(): Criteria {
|
|
|
1238
1266
|
return {
|
|
1239
1267
|
filterType: FilterType.EQUAL,
|
|
1240
1268
|
fieldType: PrimitiveType.BYTES,
|
|
1241
|
-
fieldIndex:
|
|
1269
|
+
fieldIndex: CheatCodes.ANY_ACTION_PARAM,
|
|
1242
1270
|
filterData: zeroHash,
|
|
1243
1271
|
};
|
|
1244
1272
|
}
|
|
1273
|
+
|
|
1274
|
+
/**
|
|
1275
|
+
* Creates an ActionClaimant object that represents the transaction sender as the claimant.
|
|
1276
|
+
* This function is useful when you want to set up an action where the transaction sender is always considered the valid claimant,
|
|
1277
|
+
* regardless of the event or function parameters.
|
|
1278
|
+
*
|
|
1279
|
+
* The returned ActionClaimant has the following properties:
|
|
1280
|
+
* - signatureType: Set to SignatureType.EVENT (though it doesn't matter for this case)
|
|
1281
|
+
* - signature: Set to zeroHash (0x0000...0000)
|
|
1282
|
+
* - fieldIndex: Set to 255, indicating "any" field using CheatCodes enum
|
|
1283
|
+
* - targetContract: Set to zeroAddress (0x0000...0000)
|
|
1284
|
+
* - chainid: Set to 0, indicating it's valid for any chain
|
|
1285
|
+
*
|
|
1286
|
+
* @returns {ActionClaimant} An ActionClaimant object representing the msg.sender
|
|
1287
|
+
*
|
|
1288
|
+
* @example
|
|
1289
|
+
* const eventAction = new EventAction();
|
|
1290
|
+
* const payload: EventActionPayload = {
|
|
1291
|
+
* actionClaimant: transactionSenderClaimant(),
|
|
1292
|
+
* actionSteps: [
|
|
1293
|
+
* // ... define your action steps here
|
|
1294
|
+
* ]
|
|
1295
|
+
* };
|
|
1296
|
+
* await eventAction.deploy(payload);
|
|
1297
|
+
*/
|
|
1298
|
+
export function transactionSenderClaimant(): ActionClaimant {
|
|
1299
|
+
return {
|
|
1300
|
+
signatureType: SignatureType.EVENT,
|
|
1301
|
+
signature: zeroHash,
|
|
1302
|
+
fieldIndex: CheatCodes.TX_SENDER_CLAIMANT,
|
|
1303
|
+
targetContract: zeroAddress,
|
|
1304
|
+
chainid: 0,
|
|
1305
|
+
};
|
|
1306
|
+
}
|
package/src/BoostCore.test.ts
CHANGED
|
@@ -17,6 +17,7 @@ import type { ERC20Incentive } from "./Incentives/ERC20Incentive";
|
|
|
17
17
|
import { StrategyType } from "./claiming";
|
|
18
18
|
import { BoostNotFoundError, IncentiveNotCloneableError } from "./errors";
|
|
19
19
|
import { bytes4 } from "./utils";
|
|
20
|
+
import { BoostValidatorEOA } from "./Validators/Validator";
|
|
20
21
|
|
|
21
22
|
let fixtures: Fixtures, budgets: BudgetFixtures;
|
|
22
23
|
|
|
@@ -843,7 +844,7 @@ describe("BoostCore", () => {
|
|
|
843
844
|
expect(validator.payload?.validatorCaller).toBe(core.assertValidAddress());
|
|
844
845
|
|
|
845
846
|
// expect current account to be a signer
|
|
846
|
-
const signer = await validator.signers(
|
|
847
|
+
const signer = await validator.signers(BoostValidatorEOA.TESTNET);
|
|
847
848
|
expect(signer).toBeDefined();
|
|
848
849
|
expect(signer).toBe(true);
|
|
849
850
|
});
|
|
@@ -867,12 +868,11 @@ describe("BoostCore", () => {
|
|
|
867
868
|
|
|
868
869
|
const claimant = trustedSigner.account;
|
|
869
870
|
const incentiveData = pad("0xdef456232173821931823712381232131391321934");
|
|
870
|
-
const incentiveQuantity = 1;
|
|
871
871
|
const claimDataPayload = await boost.validator.encodeClaimData({
|
|
872
872
|
signer: trustedSigner,
|
|
873
873
|
incentiveData,
|
|
874
874
|
chainId: defaultOptions.config.chains[0].id,
|
|
875
|
-
incentiveQuantity,
|
|
875
|
+
incentiveQuantity: boost.incentives.length,
|
|
876
876
|
claimant,
|
|
877
877
|
boostId: boost.id,
|
|
878
878
|
});
|
package/src/BoostCore.ts
CHANGED
|
@@ -21,6 +21,7 @@ import { bytecode } from '@boostxyz/evm/artifacts/contracts/BoostCore.sol/BoostC
|
|
|
21
21
|
import {
|
|
22
22
|
type GetTransactionReceiptParameters,
|
|
23
23
|
getAccount,
|
|
24
|
+
getChains,
|
|
24
25
|
getTransactionReceipt,
|
|
25
26
|
waitForTransactionReceipt,
|
|
26
27
|
} from '@wagmi/core';
|
|
@@ -93,17 +94,26 @@ import {
|
|
|
93
94
|
PointsIncentive,
|
|
94
95
|
type PointsIncentivePayload,
|
|
95
96
|
} from './Incentives/PointsIncentive';
|
|
97
|
+
import {
|
|
98
|
+
LimitedSignerValidator,
|
|
99
|
+
type LimitedSignerValidatorPayload,
|
|
100
|
+
} from './Validators/LimitedSignerValidator';
|
|
96
101
|
import {
|
|
97
102
|
SignerValidator,
|
|
98
103
|
type SignerValidatorPayload,
|
|
99
104
|
} from './Validators/SignerValidator';
|
|
100
|
-
import {
|
|
105
|
+
import {
|
|
106
|
+
BoostValidatorEOA,
|
|
107
|
+
type Validator,
|
|
108
|
+
validatorFromAddress,
|
|
109
|
+
} from './Validators/Validator';
|
|
101
110
|
import {
|
|
102
111
|
BoostCoreNoIdentifierEmitted,
|
|
103
112
|
BoostNotFoundError,
|
|
104
113
|
BudgetMustAuthorizeBoostCore,
|
|
105
114
|
DeployableUnknownOwnerProvidedError,
|
|
106
115
|
IncentiveNotCloneableError,
|
|
116
|
+
InvalidProtocolChainIdError,
|
|
107
117
|
MustInitializeBudgetError,
|
|
108
118
|
} from './errors';
|
|
109
119
|
import {
|
|
@@ -347,8 +357,6 @@ export class BoostCore extends Deployable<
|
|
|
347
357
|
options,
|
|
348
358
|
);
|
|
349
359
|
|
|
350
|
-
console.log(onChainPayload);
|
|
351
|
-
|
|
352
360
|
const boostHash = await boostFactory(options.config, {
|
|
353
361
|
...this.optionallyAttachAccount(options.account),
|
|
354
362
|
// biome-ignore lint/suspicious/noExplicitAny: Accept any shape of valid wagmi/viem parameters, wagmi does the same thing internally
|
|
@@ -441,9 +449,24 @@ export class BoostCore extends Deployable<
|
|
|
441
449
|
}
|
|
442
450
|
}
|
|
443
451
|
|
|
452
|
+
// If not providing a custom validator, use either Boost's mainnet or testnet EOA, depending on provided chain id and given chain configurations
|
|
444
453
|
if (!payload.validator) {
|
|
454
|
+
const chains = getChains(options.config).filter(
|
|
455
|
+
(chain) => !!this.addresses[chain.id] && chain.id === chainId,
|
|
456
|
+
);
|
|
457
|
+
const chain = chains.at(0);
|
|
458
|
+
if (!chain)
|
|
459
|
+
throw new InvalidProtocolChainIdError(
|
|
460
|
+
chainId,
|
|
461
|
+
Object.keys(this.addresses).map(Number),
|
|
462
|
+
);
|
|
463
|
+
const testnet = chain.testnet || chain.id === 31337;
|
|
445
464
|
payload.validator = this.SignerValidator({
|
|
446
|
-
signers: [
|
|
465
|
+
signers: [
|
|
466
|
+
(testnet
|
|
467
|
+
? BoostValidatorEOA.TESTNET
|
|
468
|
+
: BoostValidatorEOA.MAINNET) as unknown as Address,
|
|
469
|
+
],
|
|
447
470
|
validatorCaller: coreAddress,
|
|
448
471
|
});
|
|
449
472
|
}
|
|
@@ -800,7 +823,7 @@ export class BoostCore extends Deployable<
|
|
|
800
823
|
budget,
|
|
801
824
|
validator,
|
|
802
825
|
allowList,
|
|
803
|
-
incentives,
|
|
826
|
+
incentives: incentives as Incentive[],
|
|
804
827
|
protocolFee,
|
|
805
828
|
maxParticipants,
|
|
806
829
|
owner,
|
|
@@ -1372,6 +1395,29 @@ export class BoostCore extends Deployable<
|
|
|
1372
1395
|
);
|
|
1373
1396
|
}
|
|
1374
1397
|
|
|
1398
|
+
/**
|
|
1399
|
+
* Bound {@link LimitedSignerValidator} constructor that reuses the same configuration as the Boost Core instance.
|
|
1400
|
+
*
|
|
1401
|
+
* @example
|
|
1402
|
+
* ```ts
|
|
1403
|
+
* const validator = core.LimitedSignerValidator({ ... }) // is roughly equivalent to
|
|
1404
|
+
* const validator = new LimitedSignerValidator({ config: core._config, account: core._account }, { ... })
|
|
1405
|
+
* ```
|
|
1406
|
+
* @param {DeployablePayloadOrAddress<LimitedSignerValidatorPayload>} options
|
|
1407
|
+
* @param {?boolean} [isBase]
|
|
1408
|
+
* @returns {LimitedSignerValidator}
|
|
1409
|
+
*/
|
|
1410
|
+
LimitedSignerValidator(
|
|
1411
|
+
options: DeployablePayloadOrAddress<LimitedSignerValidatorPayload>,
|
|
1412
|
+
isBase?: boolean,
|
|
1413
|
+
) {
|
|
1414
|
+
return new LimitedSignerValidator(
|
|
1415
|
+
{ config: this._config, account: this._account },
|
|
1416
|
+
options,
|
|
1417
|
+
isBase,
|
|
1418
|
+
);
|
|
1419
|
+
}
|
|
1420
|
+
|
|
1375
1421
|
/**
|
|
1376
1422
|
* Bound {@link ERC20VariableCriteriaIncentive} constructor that reuses the same configuration as the Boost Core instance.
|
|
1377
1423
|
*
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import { loadFixture } from
|
|
2
|
-
import { isAddress, pad, parseEther, zeroAddress } from
|
|
3
|
-
import { beforeAll, describe, expect, test } from
|
|
4
|
-
import { accounts } from
|
|
1
|
+
import { loadFixture } from "@nomicfoundation/hardhat-network-helpers";
|
|
2
|
+
import { isAddress, pad, parseEther, zeroAddress } from "viem";
|
|
3
|
+
import { beforeAll, describe, expect, test } from "vitest";
|
|
4
|
+
import { accounts } from "@boostxyz/test/accounts";
|
|
5
5
|
import {
|
|
6
6
|
type Fixtures,
|
|
7
7
|
defaultOptions,
|
|
8
8
|
deployFixtures,
|
|
9
9
|
freshBoost,
|
|
10
|
-
} from
|
|
11
|
-
import { PointsIncentive } from
|
|
12
|
-
import { Roles } from
|
|
10
|
+
} from "@boostxyz/test/helpers";
|
|
11
|
+
import { PointsIncentive } from "./PointsIncentive";
|
|
12
|
+
import { Roles } from "../Deployable/DeployableTargetWithRBAC";
|
|
13
13
|
|
|
14
14
|
let fixtures: Fixtures;
|
|
15
15
|
|
|
@@ -25,15 +25,15 @@ function freshAllowList(fixtures: Fixtures) {
|
|
|
25
25
|
};
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
describe(
|
|
28
|
+
describe("AllowListIncentive", () => {
|
|
29
29
|
beforeAll(async () => {
|
|
30
30
|
fixtures = await loadFixture(deployFixtures(defaultOptions));
|
|
31
31
|
});
|
|
32
32
|
|
|
33
|
-
test(
|
|
33
|
+
test("can successfully be deployed", async () => {
|
|
34
34
|
const action = new PointsIncentive(defaultOptions, {
|
|
35
35
|
venue: zeroAddress,
|
|
36
|
-
selector:
|
|
36
|
+
selector: "0xdeadb33f",
|
|
37
37
|
reward: 1n,
|
|
38
38
|
limit: 1n,
|
|
39
39
|
});
|
|
@@ -41,7 +41,7 @@ describe('AllowListIncentive', () => {
|
|
|
41
41
|
expect(isAddress(action.assertValidAddress())).toBe(true);
|
|
42
42
|
});
|
|
43
43
|
|
|
44
|
-
test(
|
|
44
|
+
test("can claim", async () => {
|
|
45
45
|
// biome-ignore lint/style/noNonNullAssertion: we know this is defined
|
|
46
46
|
const referrer = accounts.at(1)?.account!;
|
|
47
47
|
// biome-ignore lint/style/noNonNullAssertion: we know this is defined
|
|
@@ -63,15 +63,14 @@ describe('AllowListIncentive', () => {
|
|
|
63
63
|
);
|
|
64
64
|
|
|
65
65
|
const claimant = trustedSigner.account;
|
|
66
|
-
const incentiveData = pad(
|
|
66
|
+
const incentiveData = pad("0xdef456232173821931823712381232131391321934");
|
|
67
67
|
console.log(claimant);
|
|
68
68
|
|
|
69
|
-
const incentiveQuantity = 1;
|
|
70
69
|
const claimDataPayload = await boost.validator.encodeClaimData({
|
|
71
70
|
signer: trustedSigner,
|
|
72
71
|
incentiveData,
|
|
73
72
|
chainId: defaultOptions.config.chains[0].id,
|
|
74
|
-
incentiveQuantity,
|
|
73
|
+
incentiveQuantity: boost.incentives.length,
|
|
75
74
|
claimant,
|
|
76
75
|
boostId: boost.id,
|
|
77
76
|
});
|
|
@@ -82,12 +81,12 @@ describe('AllowListIncentive', () => {
|
|
|
82
81
|
0n,
|
|
83
82
|
referrer,
|
|
84
83
|
claimDataPayload,
|
|
85
|
-
{ value: parseEther(
|
|
84
|
+
{ value: parseEther("0.000075"), account: trustedSigner.privateKey },
|
|
86
85
|
);
|
|
87
86
|
expect(await allowList.isAllowed(trustedSigner.account)).toBe(true);
|
|
88
87
|
});
|
|
89
88
|
|
|
90
|
-
test(
|
|
89
|
+
test("cannot claim twice", async () => {
|
|
91
90
|
// biome-ignore lint/style/noNonNullAssertion: we know this is defined
|
|
92
91
|
const referrer = accounts.at(1)?.account!;
|
|
93
92
|
// biome-ignore lint/style/noNonNullAssertion: we know this is defined
|
|
@@ -107,16 +106,15 @@ describe('AllowListIncentive', () => {
|
|
|
107
106
|
[allowListIncentive.assertValidAddress()],
|
|
108
107
|
[Roles.MANAGER],
|
|
109
108
|
);
|
|
110
|
-
const incentiveQuantity = 1;
|
|
111
109
|
const claimant = trustedSigner.account;
|
|
112
|
-
const incentiveData = pad(
|
|
110
|
+
const incentiveData = pad("0xdef456232173821931823712381232131391321934");
|
|
113
111
|
console.log(claimant);
|
|
114
112
|
|
|
115
113
|
const claimDataPayload = await boost.validator.encodeClaimData({
|
|
116
114
|
signer: trustedSigner,
|
|
117
115
|
incentiveData,
|
|
118
116
|
chainId: defaultOptions.config.chains[0].id,
|
|
119
|
-
incentiveQuantity,
|
|
117
|
+
incentiveQuantity: boost.incentives.length,
|
|
120
118
|
claimant,
|
|
121
119
|
boostId: boost.id,
|
|
122
120
|
});
|
|
@@ -126,7 +124,7 @@ describe('AllowListIncentive', () => {
|
|
|
126
124
|
0n,
|
|
127
125
|
referrer,
|
|
128
126
|
claimDataPayload,
|
|
129
|
-
{ value: parseEther(
|
|
127
|
+
{ value: parseEther("0.000075"), account: trustedSigner.privateKey },
|
|
130
128
|
);
|
|
131
129
|
try {
|
|
132
130
|
await fixtures.core.claimIncentive(
|
|
@@ -134,7 +132,7 @@ describe('AllowListIncentive', () => {
|
|
|
134
132
|
0n,
|
|
135
133
|
referrer,
|
|
136
134
|
claimDataPayload,
|
|
137
|
-
{ value: parseEther(
|
|
135
|
+
{ value: parseEther("0.000075"), account: trustedSigner.privateKey },
|
|
138
136
|
);
|
|
139
137
|
} catch (e) {
|
|
140
138
|
expect(e).toBeInstanceOf(Error);
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { readMockErc20BalanceOf } from
|
|
2
|
-
import { loadFixture } from
|
|
3
|
-
import { isAddress, pad, parseEther, zeroAddress } from
|
|
4
|
-
import { beforeAll, beforeEach, describe, expect, test } from
|
|
5
|
-
import { accounts } from
|
|
1
|
+
import { readMockErc20BalanceOf } from "@boostxyz/evm";
|
|
2
|
+
import { loadFixture } from "@nomicfoundation/hardhat-network-helpers";
|
|
3
|
+
import { isAddress, pad, parseEther, zeroAddress } from "viem";
|
|
4
|
+
import { beforeAll, beforeEach, describe, expect, test } from "vitest";
|
|
5
|
+
import { accounts } from "@boostxyz/test/accounts";
|
|
6
6
|
import {
|
|
7
7
|
type BudgetFixtures,
|
|
8
8
|
type Fixtures,
|
|
@@ -10,12 +10,12 @@ import {
|
|
|
10
10
|
deployFixtures,
|
|
11
11
|
freshBoost,
|
|
12
12
|
fundBudget,
|
|
13
|
-
} from
|
|
14
|
-
import { CGDAIncentive } from
|
|
13
|
+
} from "@boostxyz/test/helpers";
|
|
14
|
+
import { CGDAIncentive } from "./CGDAIncentive";
|
|
15
15
|
|
|
16
16
|
let fixtures: Fixtures, budgets: BudgetFixtures;
|
|
17
17
|
|
|
18
|
-
describe(
|
|
18
|
+
describe("CGDAIncentive", () => {
|
|
19
19
|
beforeAll(async () => {
|
|
20
20
|
fixtures = await loadFixture(deployFixtures(defaultOptions));
|
|
21
21
|
});
|
|
@@ -24,20 +24,20 @@ describe('CGDAIncentive', () => {
|
|
|
24
24
|
budgets = await loadFixture(fundBudget(defaultOptions, fixtures));
|
|
25
25
|
});
|
|
26
26
|
|
|
27
|
-
test(
|
|
27
|
+
test("can successfully be deployed", async () => {
|
|
28
28
|
const action = new CGDAIncentive(defaultOptions, {
|
|
29
29
|
asset: budgets.erc20.assertValidAddress(),
|
|
30
30
|
initialReward: 1n,
|
|
31
31
|
totalBudget: 10n,
|
|
32
32
|
rewardBoost: 1n,
|
|
33
33
|
rewardDecay: 1n,
|
|
34
|
-
manager: budgets.budget.address || zeroAddress
|
|
34
|
+
manager: budgets.budget.address || zeroAddress,
|
|
35
35
|
});
|
|
36
36
|
await action.deploy();
|
|
37
37
|
expect(isAddress(action.assertValidAddress())).toBe(true);
|
|
38
38
|
});
|
|
39
39
|
|
|
40
|
-
test(
|
|
40
|
+
test("can claim", async () => {
|
|
41
41
|
// biome-ignore lint/style/noNonNullAssertion: we know this is defined
|
|
42
42
|
const referrer = accounts.at(1)!.account!;
|
|
43
43
|
// biome-ignore lint/style/noNonNullAssertion: we know this is defined
|
|
@@ -56,14 +56,13 @@ describe('CGDAIncentive', () => {
|
|
|
56
56
|
});
|
|
57
57
|
|
|
58
58
|
const claimant = trustedSigner.account;
|
|
59
|
-
const incentiveData = pad(
|
|
59
|
+
const incentiveData = pad("0xdef456232173821931823712381232131391321934");
|
|
60
60
|
|
|
61
|
-
const incentiveQuantity = 1;
|
|
62
61
|
const claimDataPayload = await boost.validator.encodeClaimData({
|
|
63
62
|
signer: trustedSigner,
|
|
64
63
|
incentiveData,
|
|
65
64
|
chainId: defaultOptions.config.chains[0].id,
|
|
66
|
-
incentiveQuantity,
|
|
65
|
+
incentiveQuantity: boost.incentives.length,
|
|
67
66
|
claimant,
|
|
68
67
|
boostId: boost.id,
|
|
69
68
|
});
|
|
@@ -73,7 +72,7 @@ describe('CGDAIncentive', () => {
|
|
|
73
72
|
0n,
|
|
74
73
|
referrer,
|
|
75
74
|
claimDataPayload,
|
|
76
|
-
{ value: parseEther(
|
|
75
|
+
{ value: parseEther("0.000075") },
|
|
77
76
|
);
|
|
78
77
|
expect(
|
|
79
78
|
await readMockErc20BalanceOf(defaultOptions.config, {
|
|
@@ -83,7 +82,7 @@ describe('CGDAIncentive', () => {
|
|
|
83
82
|
).toBe(1n);
|
|
84
83
|
});
|
|
85
84
|
|
|
86
|
-
test(
|
|
85
|
+
test("cannot claim twice", async () => {
|
|
87
86
|
// biome-ignore lint/style/noNonNullAssertion: we know this is defined
|
|
88
87
|
const referrer = accounts.at(1)!.account!;
|
|
89
88
|
// biome-ignore lint/style/noNonNullAssertion: we know this is defined
|
|
@@ -94,7 +93,7 @@ describe('CGDAIncentive', () => {
|
|
|
94
93
|
totalBudget: 10n,
|
|
95
94
|
rewardBoost: 1n,
|
|
96
95
|
rewardDecay: 1n,
|
|
97
|
-
manager: budgets.budget.address || zeroAddress
|
|
96
|
+
manager: budgets.budget.address || zeroAddress,
|
|
98
97
|
});
|
|
99
98
|
const boost = await freshBoost(fixtures, {
|
|
100
99
|
budget: budgets.budget,
|
|
@@ -102,13 +101,12 @@ describe('CGDAIncentive', () => {
|
|
|
102
101
|
});
|
|
103
102
|
|
|
104
103
|
const claimant = trustedSigner.account;
|
|
105
|
-
const incentiveData = pad(
|
|
106
|
-
const incentiveQuantity = 1;
|
|
104
|
+
const incentiveData = pad("0xdef456232173821931823712381232131391321934");
|
|
107
105
|
const claimDataPayload = await boost.validator.encodeClaimData({
|
|
108
106
|
signer: trustedSigner,
|
|
109
107
|
incentiveData,
|
|
110
108
|
chainId: defaultOptions.config.chains[0].id,
|
|
111
|
-
incentiveQuantity,
|
|
109
|
+
incentiveQuantity: boost.incentives.length,
|
|
112
110
|
claimant,
|
|
113
111
|
boostId: boost.id,
|
|
114
112
|
});
|
|
@@ -118,7 +116,7 @@ describe('CGDAIncentive', () => {
|
|
|
118
116
|
0n,
|
|
119
117
|
referrer,
|
|
120
118
|
claimDataPayload,
|
|
121
|
-
{ value: parseEther(
|
|
119
|
+
{ value: parseEther("0.000075") },
|
|
122
120
|
);
|
|
123
121
|
try {
|
|
124
122
|
await fixtures.core.claimIncentive(
|
|
@@ -126,7 +124,7 @@ describe('CGDAIncentive', () => {
|
|
|
126
124
|
0n,
|
|
127
125
|
referrer,
|
|
128
126
|
claimDataPayload,
|
|
129
|
-
{ value: parseEther(
|
|
127
|
+
{ value: parseEther("0.000075") },
|
|
130
128
|
);
|
|
131
129
|
} catch (e) {
|
|
132
130
|
expect(e).toBeInstanceOf(Error);
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { readMockErc1155BalanceOf } from
|
|
2
|
-
import { loadFixture } from
|
|
3
|
-
import { isAddress, pad, parseEther, zeroAddress, zeroHash } from
|
|
4
|
-
import { beforeAll, beforeEach, describe, expect, test } from
|
|
5
|
-
import { accounts } from
|
|
1
|
+
import { readMockErc1155BalanceOf } from "@boostxyz/evm";
|
|
2
|
+
import { loadFixture } from "@nomicfoundation/hardhat-network-helpers";
|
|
3
|
+
import { isAddress, pad, parseEther, zeroAddress, zeroHash } from "viem";
|
|
4
|
+
import { beforeAll, beforeEach, describe, expect, test } from "vitest";
|
|
5
|
+
import { accounts } from "@boostxyz/test/accounts";
|
|
6
6
|
import {
|
|
7
7
|
type BudgetFixtures,
|
|
8
8
|
type Fixtures,
|
|
@@ -10,14 +10,14 @@ import {
|
|
|
10
10
|
deployFixtures,
|
|
11
11
|
freshBoost,
|
|
12
12
|
fundBudget,
|
|
13
|
-
} from
|
|
14
|
-
import { ERC1155Incentive, ERC1155StrategyType } from
|
|
13
|
+
} from "@boostxyz/test/helpers";
|
|
14
|
+
import { ERC1155Incentive, ERC1155StrategyType } from "./ERC1155Incentive";
|
|
15
15
|
|
|
16
|
-
const BOOST_CORE_CLAIM_FEE = parseEther(
|
|
16
|
+
const BOOST_CORE_CLAIM_FEE = parseEther("0.000075");
|
|
17
17
|
|
|
18
18
|
let fixtures: Fixtures, budgets: BudgetFixtures;
|
|
19
19
|
|
|
20
|
-
describe.skip(
|
|
20
|
+
describe.skip("ERC1155Incentive", () => {
|
|
21
21
|
beforeAll(async () => {
|
|
22
22
|
fixtures = await loadFixture(deployFixtures(defaultOptions));
|
|
23
23
|
});
|
|
@@ -26,19 +26,19 @@ describe.skip('ERC1155Incentive', () => {
|
|
|
26
26
|
budgets = await loadFixture(fundBudget(defaultOptions, fixtures));
|
|
27
27
|
});
|
|
28
28
|
|
|
29
|
-
test(
|
|
29
|
+
test("can successfully be deployed", async () => {
|
|
30
30
|
const action = new ERC1155Incentive(defaultOptions, {
|
|
31
31
|
asset: zeroAddress,
|
|
32
32
|
strategy: ERC1155StrategyType.MINT,
|
|
33
33
|
tokenId: 0n,
|
|
34
34
|
limit: 10n,
|
|
35
|
-
extraData:
|
|
35
|
+
extraData: "0x",
|
|
36
36
|
});
|
|
37
37
|
await action.deploy();
|
|
38
38
|
expect(isAddress(action.assertValidAddress())).toBe(true);
|
|
39
39
|
});
|
|
40
40
|
|
|
41
|
-
test(
|
|
41
|
+
test("can claim", async () => {
|
|
42
42
|
// biome-ignore lint/style/noNonNullAssertion: we know this is defined
|
|
43
43
|
const referrer = accounts.at(1)!.account!,
|
|
44
44
|
// biome-ignore lint/style/noNonNullAssertion: we know this is defined
|
|
@@ -59,13 +59,12 @@ describe.skip('ERC1155Incentive', () => {
|
|
|
59
59
|
});
|
|
60
60
|
|
|
61
61
|
const claimant = trustedSigner.account;
|
|
62
|
-
const incentiveData = pad(
|
|
63
|
-
const incentiveQuantity = 1;
|
|
62
|
+
const incentiveData = pad("0xdef456232173821931823712381232131391321934");
|
|
64
63
|
const claimDataPayload = await boost.validator.encodeClaimData({
|
|
65
64
|
signer: trustedSigner,
|
|
66
65
|
incentiveData,
|
|
67
66
|
chainId: defaultOptions.config.chains[0].id,
|
|
68
|
-
incentiveQuantity,
|
|
67
|
+
incentiveQuantity: boost.incentives.length,
|
|
69
68
|
claimant,
|
|
70
69
|
boostId: boost.id,
|
|
71
70
|
});
|