Skip to content

Commit 251a766

Browse files
committed
Shuffle permission decoding logic
1 parent c2c1b68 commit 251a766

27 files changed

Lines changed: 318 additions & 160 deletions

packages/7715-permission-types/src/permissions/erc20TokenAllowance.ts renamed to packages/7715-permission-types/src/permissions/caveats/erc20TokenAllowance.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
1-
import { erc20PayeeRuleDecoder } from './erc20PayeeRuleDecoder';
2-
import { expiryRule } from './expiryRule';
3-
import { redeemerRuleDecoder } from './redeemerRuleDecoder';
1+
import { expiryRule } from '../rules/expiry';
2+
import { erc20PayeeRuleDecoder } from '../rules/payee';
3+
import { redeemerRuleDecoder } from '../rules/redeemer';
44
import type {
55
ChecksumCaveat,
66
ChecksumEnforcersByChainId,
77
DecodedPermission,
88
MakePermissionDecoderConfig,
9-
} from './types';
9+
} from '../types';
1010
import {
1111
getByteLength,
1212
getTermsByEnforcer,
1313
splitHex,
1414
UINT256_MAX,
1515
ZERO_32_BYTES,
16-
} from './utils';
16+
} from '../utils';
1717

1818
/**
1919
* Builds the configuration for the erc20-token-allowance permission decoder.

packages/7715-permission-types/src/permissions/erc20TokenPeriodic.ts renamed to packages/7715-permission-types/src/permissions/caveats/erc20TokenPeriodic.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,21 @@
11
import { hexToBigInt, hexToNumber } from '@metamask/utils';
22

3-
import { erc20PayeeRuleDecoder } from './erc20PayeeRuleDecoder';
4-
import { expiryRule } from './expiryRule';
5-
import { redeemerRuleDecoder } from './redeemerRuleDecoder';
3+
import { expiryRule } from '../rules/expiry';
4+
import { erc20PayeeRuleDecoder } from '../rules/payee';
5+
import { redeemerRuleDecoder } from '../rules/redeemer';
66
import type {
77
ChecksumCaveat,
88
ChecksumEnforcersByChainId,
99
DecodedPermission,
1010
MakePermissionDecoderConfig,
11-
} from './types';
11+
} from '../types';
1212
import {
1313
getByteLength,
1414
getTermsByEnforcer,
1515
MAX_PERIOD_DURATION,
1616
splitHex,
1717
ZERO_32_BYTES,
18-
} from './utils';
18+
} from '../utils';
1919

2020
/**
2121
* Builds the configuration for the erc20-token-periodic permission decoder.

packages/7715-permission-types/src/permissions/erc20TokenRevocation.ts renamed to packages/7715-permission-types/src/permissions/caveats/erc20TokenRevocation.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
1-
import { expiryRule } from './expiryRule';
2-
import { redeemerRuleDecoder } from './redeemerRuleDecoder';
1+
import { expiryRule } from '../rules/expiry';
2+
import { redeemerRuleDecoder } from '../rules/redeemer';
33
import type {
44
ChecksumCaveat,
55
ChecksumEnforcersByChainId,
66
DecodedPermission,
77
MakePermissionDecoderConfig,
8-
} from './types';
8+
} from '../types';
99
import {
1010
ERC20_APPROVE_SELECTOR_TERMS,
1111
ERC20_APPROVE_ZERO_AMOUNT_TERMS,
1212
getTermsByEnforcer,
1313
ZERO_32_BYTES,
14-
} from './utils';
14+
} from '../utils';
1515

1616
/**
1717
* Builds the configuration for the erc20-token-revocation permission decoder.

packages/7715-permission-types/src/permissions/erc20TokenStream.ts renamed to packages/7715-permission-types/src/permissions/caveats/erc20TokenStream.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
11
import { hexToBigInt, hexToNumber } from '@metamask/utils';
22

3-
import { erc20PayeeRuleDecoder } from './erc20PayeeRuleDecoder';
4-
import { expiryRule } from './expiryRule';
5-
import { redeemerRuleDecoder } from './redeemerRuleDecoder';
3+
import { expiryRule } from '../rules/expiry';
4+
import { erc20PayeeRuleDecoder } from '../rules/payee';
5+
import { redeemerRuleDecoder } from '../rules/redeemer';
66
import type {
77
ChecksumCaveat,
88
ChecksumEnforcersByChainId,
99
DecodedPermission,
1010
MakePermissionDecoderConfig,
11-
} from './types';
11+
} from '../types';
1212
import {
1313
getByteLength,
1414
getTermsByEnforcer,
1515
splitHex,
1616
ZERO_32_BYTES,
17-
} from './utils';
17+
} from '../utils';
1818

1919
/**
2020
* Builds the configuration for the erc20-token-stream permission decoder.

packages/7715-permission-types/src/permissions/nativeTokenAllowance.ts renamed to packages/7715-permission-types/src/permissions/caveats/nativeTokenAllowance.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,21 @@
11
import { hexToNumber } from '@metamask/utils';
22

3-
import { expiryRule } from './expiryRule';
4-
import { nativePayeeRuleDecoder } from './nativePayeeRuleDecoder';
5-
import { redeemerRuleDecoder } from './redeemerRuleDecoder';
3+
import { expiryRule } from '../rules/expiry';
4+
import { nativePayeeRuleDecoder } from '../rules/payee';
5+
import { redeemerRuleDecoder } from '../rules/redeemer';
66
import type {
77
ChecksumCaveat,
88
ChecksumEnforcersByChainId,
99
DecodedPermission,
1010
MakePermissionDecoderConfig,
11-
} from './types';
11+
} from '../types';
1212
import {
1313
getByteLength,
1414
getTermsByEnforcer,
1515
splitHex,
1616
UINT256_MAX,
1717
ZERO_32_BYTES,
18-
} from './utils';
18+
} from '../utils';
1919

2020
/**
2121
* Builds the configuration for the native-token-allowance permission decoder.

packages/7715-permission-types/src/permissions/nativeTokenPeriodic.ts renamed to packages/7715-permission-types/src/permissions/caveats/nativeTokenPeriodic.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
11
import { hexToBigInt, hexToNumber } from '@metamask/utils';
22

3-
import { expiryRule } from './expiryRule';
4-
import { nativePayeeRuleDecoder } from './nativePayeeRuleDecoder';
5-
import { redeemerRuleDecoder } from './redeemerRuleDecoder';
3+
import { expiryRule } from '../rules/expiry';
4+
import { nativePayeeRuleDecoder } from '../rules/payee';
5+
import { redeemerRuleDecoder } from '../rules/redeemer';
66
import type {
77
ChecksumCaveat,
88
ChecksumEnforcersByChainId,
99
DecodedPermission,
1010
MakePermissionDecoderConfig,
11-
} from './types';
11+
} from '../types';
1212
import {
1313
getByteLength,
1414
getTermsByEnforcer,
1515
MAX_PERIOD_DURATION,
1616
splitHex,
17-
} from './utils';
17+
} from '../utils';
1818

1919
/**
2020
* Builds the configuration for the native-token-periodic permission decoder.

packages/7715-permission-types/src/permissions/nativeTokenStream.ts renamed to packages/7715-permission-types/src/permissions/caveats/nativeTokenStream.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
import { hexToBigInt, hexToNumber } from '@metamask/utils';
22

3-
import { expiryRule } from './expiryRule';
4-
import { nativePayeeRuleDecoder } from './nativePayeeRuleDecoder';
5-
import { redeemerRuleDecoder } from './redeemerRuleDecoder';
3+
import { expiryRule } from '../rules/expiry';
4+
import { nativePayeeRuleDecoder } from '../rules/payee';
5+
import { redeemerRuleDecoder } from '../rules/redeemer';
66
import type {
77
ChecksumCaveat,
88
ChecksumEnforcersByChainId,
99
DecodedPermission,
1010
MakePermissionDecoderConfig,
11-
} from './types';
12-
import { getByteLength, getTermsByEnforcer, splitHex } from './utils';
11+
} from '../types';
12+
import { getByteLength, getTermsByEnforcer, splitHex } from '../utils';
1313

1414
/**
1515
* Builds the configuration for the native-token-stream permission decoder.

packages/7715-permission-types/src/permissions/tokenApprovalRevocation.ts renamed to packages/7715-permission-types/src/permissions/caveats/tokenApprovalRevocation.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
/* eslint-disable no-bitwise */
22
import { hexToNumber } from '@metamask/utils';
33

4-
import { expiryRule } from './expiryRule';
4+
import { expiryRule } from '../rules/expiry';
55
import type {
66
ChecksumCaveat,
77
ChecksumEnforcersByChainId,
88
DecodedPermission,
99
MakePermissionDecoderConfig,
10-
} from './types';
11-
import { getTermsByEnforcer } from './utils';
10+
} from '../types';
11+
import { getTermsByEnforcer } from '../utils';
1212

1313
enum ApprovalRevocationFlag {
1414
Erc20Approve = 0x01,

packages/7715-permission-types/src/permissions/index.ts

Lines changed: 150 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,21 @@
1-
import { makeErc20TokenAllowanceDecoderConfig } from './erc20TokenAllowance';
2-
import { makeErc20TokenPeriodicDecoderConfig } from './erc20TokenPeriodic';
3-
import { makeErc20TokenRevocationDecoderConfig } from './erc20TokenRevocation';
4-
import { makeErc20TokenStreamDecoderConfig } from './erc20TokenStream';
5-
import { makeNativeTokenAllowanceDecoderConfig } from './nativeTokenAllowance';
6-
import { makeNativeTokenPeriodicDecoderConfig } from './nativeTokenPeriodic';
7-
import { makeNativeTokenStreamDecoderConfig } from './nativeTokenStream';
8-
import { makeTokenApprovalRevocationDecoderConfig } from './tokenApprovalRevocation';
9-
import type { DeployedContractsByName, PermissionDecoderConfig } from './types';
1+
import { getChecksumAddress } from '@metamask/utils';
2+
3+
import { makeErc20TokenAllowanceDecoderConfig } from './caveats/erc20TokenAllowance';
4+
import { makeErc20TokenPeriodicDecoderConfig } from './caveats/erc20TokenPeriodic';
5+
import { makeErc20TokenRevocationDecoderConfig } from './caveats/erc20TokenRevocation';
6+
import { makeErc20TokenStreamDecoderConfig } from './caveats/erc20TokenStream';
7+
import { makeNativeTokenAllowanceDecoderConfig } from './caveats/nativeTokenAllowance';
8+
import { makeNativeTokenPeriodicDecoderConfig } from './caveats/nativeTokenPeriodic';
9+
import { makeNativeTokenStreamDecoderConfig } from './caveats/nativeTokenStream';
10+
import { makeTokenApprovalRevocationDecoderConfig } from './caveats/tokenApprovalRevocation';
11+
import type {
12+
Caveat,
13+
DeployedContractsByName,
14+
PermissionDecoder,
15+
PermissionDecoderConfig,
16+
ValidateAndDecodeResult,
17+
} from './types';
18+
import type { Hex } from '../types';
1019
import { getChecksumEnforcersByChainId } from './utils';
1120

1221
export type {
@@ -16,8 +25,40 @@ export type {
1625
DeployedContractsByName,
1726
DecodedPermission,
1827
MakePermissionDecoderConfig,
28+
PermissionDecoder,
29+
PermissionDecoderConfig,
30+
PermissionDecoderSpec,
31+
PermissionType,
32+
RuleDecoder,
33+
ValidateAndDecodeResult,
1934
} from './types';
2035

36+
export type { ExpiryRule } from './rules/expiry';
37+
export type { PayeeRule } from './rules/payee';
38+
export type { RedeemerRule } from './rules/redeemer';
39+
40+
export {
41+
EXECUTION_PERMISSION_EXPIRY_RULE_TYPE,
42+
expiryRule,
43+
} from './rules/expiry';
44+
export {
45+
EXECUTION_PERMISSION_PAYEE_RULE_TYPE,
46+
erc20PayeeRuleDecoder,
47+
nativePayeeRuleDecoder,
48+
} from './rules/payee';
49+
export {
50+
EXECUTION_PERMISSION_REDEEMER_RULE_TYPE,
51+
redeemerRuleDecoder,
52+
} from './rules/redeemer';
53+
export { makeNativeTokenStreamDecoderConfig } from './caveats/nativeTokenStream';
54+
export { makeNativeTokenPeriodicDecoderConfig } from './caveats/nativeTokenPeriodic';
55+
export { makeNativeTokenAllowanceDecoderConfig } from './caveats/nativeTokenAllowance';
56+
export { makeErc20TokenStreamDecoderConfig } from './caveats/erc20TokenStream';
57+
export { makeErc20TokenPeriodicDecoderConfig } from './caveats/erc20TokenPeriodic';
58+
export { makeErc20TokenAllowanceDecoderConfig } from './caveats/erc20TokenAllowance';
59+
export { makeErc20TokenRevocationDecoderConfig } from './caveats/erc20TokenRevocation';
60+
export { makeTokenApprovalRevocationDecoderConfig } from './caveats/tokenApprovalRevocation';
61+
2162
/**
2263
* Builds the canonical set of permission decoders for a chain.
2364
*
@@ -40,3 +81,103 @@ export const makePermissionDecoderConfigs = (
4081
makeTokenApprovalRevocationDecoderConfig(contractAddresses),
4182
];
4283
};
84+
85+
/**
86+
* Creates a runtime decoder from one permission decoder configuration.
87+
*
88+
* @param config - Permission decoder configuration.
89+
* @returns Permission decoder.
90+
*/
91+
export const makePermissionDecoder = (
92+
config: PermissionDecoderConfig,
93+
): PermissionDecoder => {
94+
const requiredEnforcers = new Map<Hex, number>(
95+
Object.entries(config.requiredEnforcers) as [Hex, number][],
96+
);
97+
const optionalEnforcers = new Set(config.optionalEnforcers);
98+
99+
const caveatAddressesMatch = (caveatAddresses: Hex[]): boolean => {
100+
const counts = new Map<Hex, number>();
101+
102+
for (const address of caveatAddresses) {
103+
counts.set(address, (counts.get(address) ?? 0) + 1);
104+
}
105+
for (const [address, count] of counts) {
106+
const maxAllowedCount =
107+
requiredEnforcers.get(address) ??
108+
(optionalEnforcers.has(address) ? 1 : 0);
109+
if (maxAllowedCount === 0 || count > maxAllowedCount) {
110+
return false;
111+
}
112+
}
113+
114+
return true;
115+
};
116+
117+
const validateAndDecodePermission = (
118+
caveats: Caveat[],
119+
): ValidateAndDecodeResult => {
120+
try {
121+
const normalizedCaveats = caveats.map((caveat) => ({
122+
...caveat,
123+
enforcer: getChecksumAddress(caveat.enforcer),
124+
}));
125+
126+
const caveatAddresses = normalizedCaveats.map(
127+
(caveat) => caveat.enforcer,
128+
);
129+
130+
if (!caveatAddressesMatch(caveatAddresses)) {
131+
throw new Error('Invalid caveats');
132+
}
133+
134+
const data = config.validateAndDecodeData(
135+
normalizedCaveats,
136+
config.contractAddresses,
137+
);
138+
const rules = config.rules
139+
.map((decodeRule) =>
140+
decodeRule({
141+
contractAddresses: config.contractAddresses,
142+
caveats: normalizedCaveats,
143+
requiredEnforcers,
144+
}),
145+
)
146+
.filter((rule) => rule !== null);
147+
const expiryRule = rules.find((rule) => rule.type === 'expiry');
148+
149+
return {
150+
isValid: true,
151+
expiry:
152+
expiryRule?.type === 'expiry' ? expiryRule.data.timestamp : null,
153+
data,
154+
rules: rules.length > 0 ? rules : undefined,
155+
};
156+
} catch (error) {
157+
return {
158+
isValid: false,
159+
error: error instanceof Error ? error : new Error('Invalid caveats'),
160+
};
161+
}
162+
};
163+
164+
return {
165+
permissionType: config.permissionType,
166+
requiredEnforcers,
167+
optionalEnforcers,
168+
caveatAddressesMatch,
169+
validateAndDecodePermission,
170+
};
171+
};
172+
173+
/**
174+
* Creates permission decoders for all supported permission types.
175+
*
176+
* @param contracts - Deployed delegation framework contracts for one chain.
177+
* @returns Runtime permission decoders.
178+
*/
179+
export const createPermissionDecodersForContracts = (
180+
contracts: DeployedContractsByName,
181+
): PermissionDecoder[] => {
182+
return makePermissionDecoderConfigs(contracts).map(makePermissionDecoder);
183+
};

0 commit comments

Comments
 (0)