Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/delegation-deployments/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
"@metamask/auto-changelog": "^5.0.2",
"compare-versions": "^6.1.1",
"tsup": "^8.5.0",
"tsx": "^4.20.5",
"typescript": "5.0.4"
}
}
1 change: 1 addition & 0 deletions packages/delegation-toolkit/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@
"ts-node": "^10.9.2",
"tsconfig-paths": "^4.2.0",
"tsup": "^8.5.0",
"tsx": "^4.20.5",
"typescript": "5.0.4",
"viem": "2.31.4",
"vitest": "^3.2.4"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ export type TokenPeriodConfig = {
startDate: number;
};

export type MultiTokenPeriodBuilderConfig = TokenPeriodConfig[];
export type MultiTokenPeriodBuilderConfig = {
tokenConfigs: TokenPeriodConfig[];
};

export const multiTokenPeriod = 'multiTokenPeriod';

Expand All @@ -32,27 +34,30 @@ export const multiTokenPeriod = 'multiTokenPeriod';
* Each token can have its own period amount, duration, and start date.
*
* @param environment - The DeleGator environment.
* @param configs - The configurations for the MultiTokenPeriodBuilder.
* @param config - The configuration for the MultiTokenPeriodBuilder.
* @param config.tokenConfigs - The token configurations for the MultiTokenPeriodBuilder.
* @returns The caveat object for the MultiTokenPeriodEnforcer.
*/
export const multiTokenPeriodBuilder = (
environment: DeleGatorEnvironment,
configs: MultiTokenPeriodBuilderConfig,
config: MultiTokenPeriodBuilderConfig,
): Caveat => {
if (!configs || configs.length === 0) {
throw new Error('MultiTokenPeriodBuilder: configs array cannot be empty');
if (!config?.tokenConfigs || config.tokenConfigs.length === 0) {
throw new Error(
'MultiTokenPeriodBuilder: tokenConfigs array cannot be empty',
);
}

configs.forEach((config) => {
if (!isAddress(config.token)) {
throw new Error(`Invalid token address: ${String(config.token)}`);
config.tokenConfigs.forEach((tokenConfig) => {
if (!isAddress(tokenConfig.token)) {
throw new Error(`Invalid token address: ${String(tokenConfig.token)}`);
}

if (config.periodAmount <= 0) {
if (tokenConfig.periodAmount <= 0) {
throw new Error('Invalid period amount: must be greater than 0');
}

if (config.periodDuration <= 0) {
if (tokenConfig.periodDuration <= 0) {
throw new Error('Invalid period duration: must be greater than 0');
}
});
Expand All @@ -62,7 +67,7 @@ export const multiTokenPeriodBuilder = (
// - 32 bytes for periodAmount
// - 32 bytes for periodDuration
// - 32 bytes for startDate
const termsArray = configs.reduce<Hex[]>(
const termsArray = config.tokenConfigs.reduce<Hex[]>(
(acc, { token, periodAmount, periodDuration, startDate }) => [
...acc,
pad(token, { size: 20 }),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { Hex } from 'viem';
import { concat, size, slice, toHex } from 'viem';
import { concat, pad, size, slice, toHex } from 'viem';
import { expect, describe, it } from 'vitest';

import type { TokenPeriodConfig } from '../../src/caveatBuilder/multiTokenPeriodBuilder';
Expand Down Expand Up @@ -38,34 +38,37 @@ describe('multiTokenPeriodBuilder', () => {
});

it('should encode a single token configuration correctly', () => {
const result = multiTokenPeriodBuilder(mockEnvironment, [mockConfig]);
const result = multiTokenPeriodBuilder(mockEnvironment, {
tokenConfigs: [mockConfig],
});

expect(size(result.terms)).to.equal(LENGTH_PER_CONFIG);

expect(slice(result.terms, 0, 20)).to.equal(mockConfig.token);
expect(slice(result.terms, 0, 20)).to.equal(
pad(mockConfig.token, { size: 20 }),
);
expect(result.enforcer).to.equal(
mockEnvironment.caveatEnforcers.MultiTokenPeriodEnforcer,
);
expect(result.args).to.equal('0x');
});

it('should encode multiple token correctly', () => {
const result = multiTokenPeriodBuilder(mockEnvironment as any, [
mockConfig,
secondMockConfig,
]);
const result = multiTokenPeriodBuilder(mockEnvironment as any, {
tokenConfigs: [mockConfig, secondMockConfig],
});

expect(result.enforcer).to.equal(
mockEnvironment.caveatEnforcers.MultiTokenPeriodEnforcer,
);
expect(result.args).to.equal('0x');
expect(result.terms).to.equal(
concat([
mockConfig.token,
pad(mockConfig.token, { size: 20 }),
toHex(mockConfig.periodAmount, { size: 32 }),
toHex(mockConfig.periodDuration, { size: 32 }),
toHex(mockConfig.startDate, { size: 32 }),
secondMockConfig.token,
pad(secondMockConfig.token, { size: 20 }),
toHex(secondMockConfig.periodAmount, { size: 32 }),
toHex(secondMockConfig.periodDuration, { size: 32 }),
toHex(secondMockConfig.startDate, { size: 32 }),
Expand All @@ -74,15 +77,15 @@ describe('multiTokenPeriodBuilder', () => {
});

it('should throw error for empty configs array', () => {
expect(() => multiTokenPeriodBuilder(mockEnvironment as any, [])).to.throw(
'MultiTokenPeriodBuilder: configs array cannot be empty',
);
expect(() =>
multiTokenPeriodBuilder(mockEnvironment as any, { tokenConfigs: [] }),
).to.throw('MultiTokenPeriodBuilder: tokenConfigs array cannot be empty');
});

it('should encode numeric values correctly', () => {
const result = multiTokenPeriodBuilder(mockEnvironment as any, [
mockConfig,
]);
const result = multiTokenPeriodBuilder(mockEnvironment as any, {
tokenConfigs: [mockConfig],
});
expect(slice(result.terms, 20, 20 + 32)).to.equal(
toHex(mockConfig.periodAmount, { size: 32 }),
);
Expand All @@ -92,34 +95,33 @@ describe('multiTokenPeriodBuilder', () => {
});

it('should validate terms length is multiple of 116 bytes', () => {
const result = multiTokenPeriodBuilder(mockEnvironment as any, [
mockConfig,
secondMockConfig,
]);
const result = multiTokenPeriodBuilder(mockEnvironment as any, {
tokenConfigs: [mockConfig, secondMockConfig],
});
expect(size(result.terms)).to.equal(LENGTH_PER_CONFIG * 2);
});

it('should throw error for invalid token address', () => {
expect(() =>
multiTokenPeriodBuilder(mockEnvironment as any, [
{ ...mockConfig, token: 'invalid' as Hex },
]),
multiTokenPeriodBuilder(mockEnvironment as any, {
tokenConfigs: [{ ...mockConfig, token: 'invalid' as Hex }],
}),
).to.throw('Invalid token address: invalid');
});

it('should throw error for invalid period amount', () => {
expect(() =>
multiTokenPeriodBuilder(mockEnvironment as any, [
{ ...mockConfig, periodAmount: 0n },
]),
multiTokenPeriodBuilder(mockEnvironment as any, {
tokenConfigs: [{ ...mockConfig, periodAmount: 0n }],
}),
).to.throw('Invalid period amount: must be greater than 0');
});

it('should throw error for invalid period duration', () => {
expect(() =>
multiTokenPeriodBuilder(mockEnvironment as any, [
{ ...mockConfig, periodDuration: 0 },
]),
multiTokenPeriodBuilder(mockEnvironment as any, {
tokenConfigs: [{ ...mockConfig, periodDuration: 0 }],
}),
).to.throw('Invalid period duration: must be greater than 0');
});
});
2 changes: 1 addition & 1 deletion packages/delegator-e2e/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"@metamask/delegation-toolkit": "workspace:*",
"dotenv": "^16.3.1",
"permissionless": "^0.1.44",
"tsx": "^4.19.3",
"tsx": "4.20.5",
"vitest": "^3.2.4"
},
"scripts": {
Expand Down
74 changes: 41 additions & 33 deletions packages/delegator-e2e/test/caveats/caveatUtils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -623,16 +623,18 @@ describe('MultiTokenPeriodEnforcer', () => {
delegator: aliceSmartAccount.address,
authority: ROOT_AUTHORITY as Address,
caveats: createCaveatBuilder(aliceSmartAccount.environment)
.addCaveat('multiTokenPeriod', [
{
token: erc20TokenAddress,
periodAmount,
periodDuration,
startDate: currentTime,
},
])
.addCaveat('multiTokenPeriod', {
tokenConfigs: [
{
token: erc20TokenAddress,
periodAmount,
periodDuration,
startDate: currentTime,
},
],
})
.build(),
salt: '0x1' as Hex,
salt: '0x1' as const,
signature: '0x1',
};

Expand Down Expand Up @@ -719,14 +721,16 @@ describe('MultiTokenPeriodEnforcer', () => {
delegator: aliceSmartAccount.address,
authority: ROOT_AUTHORITY as Address,
caveats: createCaveatBuilder(aliceSmartAccount.environment)
.addCaveat('multiTokenPeriod', [
{
token: erc20TokenAddress,
periodAmount,
periodDuration,
startDate: futureStartDate,
},
])
.addCaveat('multiTokenPeriod', {
tokenConfigs: [
{
token: erc20TokenAddress,
periodAmount,
periodDuration,
startDate: futureStartDate,
},
],
})
.build(),
salt: '0x1' as Hex,
signature: '0x1',
Expand Down Expand Up @@ -805,14 +809,16 @@ describe('MultiTokenPeriodEnforcer', () => {
delegator: aliceSmartAccount.address,
authority: ROOT_AUTHORITY as Address,
caveats: createCaveatBuilder(aliceSmartAccount.environment)
.addCaveat('multiTokenPeriod', [
{
token: erc20TokenAddress,
periodAmount,
periodDuration,
startDate,
},
])
.addCaveat('multiTokenPeriod', {
tokenConfigs: [
{
token: erc20TokenAddress,
periodAmount,
periodDuration,
startDate,
},
],
})
.build(),
salt: '0x1' as Hex,
signature: '0x1',
Expand Down Expand Up @@ -1501,14 +1507,16 @@ describe('Individual action functions vs client extension methods', () => {
delegator: aliceSmartAccount.address,
authority: ROOT_AUTHORITY as Address,
caveats: createCaveatBuilder(aliceSmartAccount.environment)
.addCaveat('multiTokenPeriod', [
{
token: erc20TokenAddress,
periodAmount,
periodDuration,
startDate: currentTime,
},
])
.addCaveat('multiTokenPeriod', {
tokenConfigs: [
{
token: erc20TokenAddress,
periodAmount,
periodDuration,
startDate: currentTime,
},
],
})
.build(),
salt: '0x1' as Hex,
signature: '0x1',
Expand Down
4 changes: 2 additions & 2 deletions packages/delegator-e2e/test/caveats/multiTokenPeriod.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ const runTest_expectSuccess = async (
authority: ROOT_AUTHORITY,
salt: '0x0',
caveats: createCaveatBuilder(environment)
.addCaveat('multiTokenPeriod', configs)
.addCaveat('multiTokenPeriod', { tokenConfigs: configs })
.build(),
signature: '0x',
};
Expand Down Expand Up @@ -215,7 +215,7 @@ const runTest_expectFailure = async (
authority: ROOT_AUTHORITY,
salt: '0x0',
caveats: createCaveatBuilder(environment)
.addCaveat('multiTokenPeriod', configs)
.addCaveat('multiTokenPeriod', { tokenConfigs: configs })
.build(),
signature: '0x',
};
Expand Down
12 changes: 7 additions & 5 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -601,6 +601,7 @@ __metadata:
"@metamask/auto-changelog": "npm:^5.0.2"
compare-versions: "npm:^6.1.1"
tsup: "npm:^8.5.0"
tsx: "npm:^4.20.5"
typescript: "npm:5.0.4"
languageName: unknown
linkType: soft
Expand Down Expand Up @@ -633,6 +634,7 @@ __metadata:
ts-node: "npm:^10.9.2"
tsconfig-paths: "npm:^4.2.0"
tsup: "npm:^8.5.0"
tsx: "npm:^4.20.5"
typescript: "npm:5.0.4"
viem: "npm:2.31.4"
vitest: "npm:^3.2.4"
Expand All @@ -649,7 +651,7 @@ __metadata:
"@metamask/delegation-toolkit": "workspace:*"
dotenv: "npm:^16.3.1"
permissionless: "npm:^0.1.44"
tsx: "npm:^4.19.3"
tsx: "npm:4.20.5"
vitest: "npm:^3.2.4"
peerDependencies:
viem: ^2.31.4
Expand Down Expand Up @@ -6576,9 +6578,9 @@ __metadata:
languageName: node
linkType: hard

"tsx@npm:^4.19.3":
version: 4.19.3
resolution: "tsx@npm:4.19.3"
"tsx@npm:4.20.5, tsx@npm:^4.20.5":
version: 4.20.5
resolution: "tsx@npm:4.20.5"
dependencies:
esbuild: "npm:~0.25.0"
fsevents: "npm:~2.3.3"
Expand All @@ -6588,7 +6590,7 @@ __metadata:
optional: true
bin:
tsx: dist/cli.mjs
checksum: 10/a7e7f41e5593b242772050abacf51908aa8a6d4d9ea6c29e80161eb557d664a0f4cc8d38d0c8c151fddb6c2e9e337af27ba0e269c9707ccd7eeff0e0ea7fcf98
checksum: 10/161420678027c43d07b60b7b6b512cc67ff86ae3cca0641a19b0d3e742c5e262bca57034c4bff6d9346f9269e9ada24b6030e1d2bc890df5e1a9754865d3c08a
languageName: node
linkType: hard

Expand Down
Loading