Skip to content

Commit a866d3e

Browse files
CoveMBericglauCopilot
authored
Plat 6356 refined type support (#510)
Co-authored-by: Eric Lau <[email protected]> Co-authored-by: Copilot <[email protected]>
1 parent 7112627 commit a866d3e

File tree

13 files changed

+323
-126
lines changed

13 files changed

+323
-126
lines changed

.gitignore

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,3 @@ node_modules
55
.env
66
.env.local
77
.vscode/settings.json
8-
9-
*deno.lock

.vscode/example.settings.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,5 @@
66
"packages/ui/scripts/development-server.ts"
77
],
88
"deno.enable": true,
9-
"deno.path": "/opt/homebrew/bin/deno" // Update this to your Deno path
9+
"deno.path": "/opt/homebrew/bin/deno" // Update this to your Deno path
1010
}

netlify.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ publish = "packages/ui/public"
44

55
edge_functions = "packages/ui/api"
66

7+
[functions]
8+
deno_import_map = "packages/ui/import_map.json"
9+
710
[[edge_functions]]
811
path = "/ai"
912
function = "ai"

packages/ui/api/ai-assistant/function-definitions/shared.ts

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import type { AiFunctionPropertyDefinition } from '../types/function-definition.ts';
2+
13
const sharedFunctionDescription = {
24
name: { type: 'string', description: 'The name of the contract' },
35

@@ -18,12 +20,19 @@ const sharedFunctionDescription = {
1820
type: 'boolean',
1921
description: 'Whether privileged accounts will be able to create more supply or emit more tokens',
2022
},
21-
} as const;
23+
} as const satisfies AiFunctionPropertyDefinition<{
24+
name: string;
25+
symbol: string;
26+
burnable: boolean;
27+
pausable: boolean;
28+
mintable: boolean;
29+
}>['properties'];
2230

2331
export const addFunctionPropertiesFrom = <
24-
TCommonOptions extends Record<string, unknown>,
25-
TCommonOptionName extends keyof (typeof sharedFunctionDescription &
26-
TCommonOptions) = keyof (typeof sharedFunctionDescription & TCommonOptions),
32+
TContract,
33+
TCommonOptions extends Record<string, unknown> = Record<string, unknown>,
34+
TCommonOptionName extends keyof (typeof sharedFunctionDescription & TCommonOptions) &
35+
keyof TContract = keyof (typeof sharedFunctionDescription & TCommonOptions) & keyof TContract,
2736
>(
2837
commonOptions: TCommonOptions,
2938
commonOptionNames: TCommonOptionName[],
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import type { AiFunctionPropertyDefinition } from '../types/function-definition.ts';
2+
import type { SolidityCommonOptions } from '../types/languages.ts';
3+
4+
export const commonFunctionDescription = {
5+
access: {
6+
anyOf: [
7+
{ type: 'string', enum: ['ownable', 'roles', 'managed'] },
8+
{ type: 'boolean', enum: [false] },
9+
],
10+
description:
11+
'The type of access control to provision. Ownable is a simple mechanism with a single account authorized for all privileged actions. Roles is a flexible mechanism with a separate role for each privileged action. A role can have many authorized accounts. Managed enables a central contract to define a policy that allows certain callers to access certain functions.',
12+
},
13+
14+
upgradeable: {
15+
anyOf: [
16+
{ type: 'string', enum: ['transparent', 'uups'] },
17+
{ type: 'boolean', enum: [false] },
18+
],
19+
description:
20+
'Whether the smart contract is upgradeable. Transparent uses more complex proxy with higher overhead, requires less changes in your contract.Can also be used with beacons. UUPS uses simpler proxy with less overhead, requires including extra code in your contract. Allows flexibility for authorizing upgrades.',
21+
},
22+
23+
info: {
24+
type: 'object',
25+
description: 'Metadata about the contract and author',
26+
properties: {
27+
securityContact: {
28+
type: 'string',
29+
description:
30+
'Email where people can contact you to report security issues. Will only be visible if contract metadata is verified.',
31+
},
32+
license: {
33+
type: 'string',
34+
description: 'The license used by the contract, default is "MIT"',
35+
},
36+
},
37+
},
38+
} as const satisfies AiFunctionPropertyDefinition<SolidityCommonOptions>['properties'];

packages/ui/api/ai-assistant/function-definitions/solidity.ts

Lines changed: 6 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,6 @@
1-
import type { AiFunctionDefinition, AiFunctionPropertyDefinition } from '../types/function-definition.ts';
1+
import type { AiFunctionDefinition } from '../types/function-definition.ts';
22
import { addFunctionPropertiesFrom } from './shared.ts';
3-
import type { SolidityCommonOptions } from '../types/languages.ts';
4-
5-
const commonFunctionDescription = {
6-
access: {
7-
anyOf: [
8-
{ type: 'boolean', enum: [false] },
9-
{ type: 'string', enum: ['ownable', 'roles', 'managed'] },
10-
],
11-
description:
12-
'The type of access control to provision. Ownable is a simple mechanism with a single account authorized for all privileged actions. Roles is a flexible mechanism with a separate role for each privileged action. A role can have many authorized accounts. Managed enables a central contract to define a policy that allows certain callers to access certain functions.',
13-
},
14-
15-
upgradeable: {
16-
anyOf: [
17-
{ type: 'boolean', enum: [false] },
18-
{ type: 'string', enum: ['transparent', 'uups'] },
19-
],
20-
description:
21-
'Whether the smart contract is upgradeable. Transparent uses more complex proxy with higher overhead, requires less changes in your contract.Can also be used with beacons. UUPS uses simpler proxy with less overhead, requires including extra code in your contract. Allows flexibility for authorizing upgrades.',
22-
},
23-
24-
info: {
25-
type: 'object',
26-
description: 'Metadata about the contract and author',
27-
properties: {
28-
securityContact: {
29-
type: 'string',
30-
description:
31-
'Email where people can contact you to report security issues. Will only be visible if contract metadata is verified.',
32-
},
33-
license: {
34-
type: 'string',
35-
description: 'The license used by the contract, default is "MIT"',
36-
},
37-
},
38-
},
39-
} as const satisfies AiFunctionPropertyDefinition<SolidityCommonOptions>['properties'];
3+
import { commonFunctionDescription } from './solidity-shared.ts';
404

415
export const erc20Function = {
426
name: 'ERC20',
@@ -65,7 +29,7 @@ export const erc20Function = {
6529
},
6630
votes: {
6731
anyOf: [
68-
{ type: 'boolean', enum: [false] },
32+
{ type: 'boolean', enum: [false, true] },
6933
{ type: 'string', enum: ['blocknumber', 'timestamp'] },
7034
],
7135
description:
@@ -135,7 +99,7 @@ export const erc721Function = {
13599
{ type: 'string', enum: ['blocknumber', 'timestamp'] },
136100
],
137101
description:
138-
'Whether to keep track of individual units for voting in on-chain governance. Voting durations can be expressed as block numbers or timestamps.',
102+
'Whether to keep track of individual units for voting in on-chain governance. Voting durations can be expressed as block numbers or timestamps (defaulting to block number if not specified).',
139103
},
140104
},
141105
required: ['name', 'symbol'],
@@ -199,7 +163,7 @@ export const stablecoinFunction = {
199163
'Whether to restrict certain users from transferring tokens, either via allowing or blocking them. This feature is experimental, not audited and is subject to change.',
200164
},
201165
upgradeable: {
202-
type: 'string',
166+
type: 'boolean',
203167
enum: [false],
204168
description: 'Upgradeability is not yet available for features that use @openzeppelin/community-contracts',
205169
},
@@ -270,8 +234,8 @@ export const governorFunction = {
270234
},
271235
timelock: {
272236
anyOf: [
273-
{ type: 'boolean', enum: [false] },
274237
{ type: 'string', enum: ['openzeppelin', 'compound'] },
238+
{ type: 'boolean', enum: [false] },
275239
],
276240
description: 'The type of timelock to use',
277241
},

packages/ui/api/ai-assistant/types/function-definition.ts

Lines changed: 12 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,28 @@
11
import type {
2-
AllLanguagesContractsOptions,
3-
LanguageContractsOptions,
4-
SupportedLanguage,
5-
AllLanguageContractsNames,
6-
} from './languages.ts';
7-
8-
type IsPrimitiveUnion<T, U = T> = [T] extends [never]
9-
? false // Edge case for `never`
10-
: [T] extends [boolean]
11-
? false
12-
: T extends U
13-
? [U] extends [T]
14-
? false
15-
: true
16-
: false;
17-
18-
type AiFunctionCallPrimaryType<TType> = TType extends string
19-
? 'string'
20-
: TType extends number
21-
? 'number'
22-
: TType extends boolean
23-
? 'boolean'
24-
: TType extends unknown[]
25-
? 'array'
26-
: TType extends object
27-
? 'object'
28-
: never;
2+
AiFunctionCallAnyOf,
3+
StringifyPrimaryType,
4+
ExactRequiredKeys,
5+
IsPrimitiveUnion,
6+
Permutation,
7+
} from './helpers.ts';
8+
import type { AllLanguagesContractsOptions, LanguageContractsOptions, SupportedLanguage } from './languages.ts';
299

3010
type AiFunctionType<TType> = {
31-
type?: AiFunctionCallPrimaryType<TType>;
32-
enum?: TType[];
11+
type?: StringifyPrimaryType<TType>;
12+
enum?: Permutation<TType>;
3313
description: string;
3414
};
3515

3616
type AiFunctionCallOneOfType<TType> =
3717
| Required<AiFunctionType<TType>>
3818
| {
39-
anyOf: Omit<AiFunctionCallType<TType>, 'description'>[];
19+
anyOf: AiFunctionCallAnyOf<TType>;
4020
description: string;
4121
};
4222

4323
export type AiFunctionCallType<TType> = AiFunctionType<TType> | AiFunctionCallOneOfType<TType>;
4424

45-
type AiFunctionProperties<TProperties extends object> = Required<{
25+
export type AiFunctionProperties<TProperties extends object> = Required<{
4626
[K in keyof TProperties]: TProperties[K] extends object
4727
? AiFunctionPropertyDefinition<TProperties[K]>
4828
: IsPrimitiveUnion<TProperties[K]> extends true
@@ -71,7 +51,7 @@ export type AiFunctionDefinition<
7151
name: TContractName;
7252
description: string;
7353
parameters: AiFunctionPropertyDefinition<Required<LanguageContractsOptions<TLanguage>[TContractName]>, TOmit> & {
74-
required?: (keyof LanguageContractsOptions<TLanguage>[TContractName])[];
54+
required?: ExactRequiredKeys<Omit<LanguageContractsOptions<TLanguage>[TContractName], 'kind' | TOmit>>;
7555
additionalProperties: false;
7656
};
7757
};
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
export type IsPrimitiveUnion<T, U = T> = [T] extends [never]
2+
? false
3+
: [T] extends [boolean]
4+
? false
5+
: T extends U
6+
? [U] extends [T]
7+
? false
8+
: true
9+
: false;
10+
11+
export type Permutation<T, K = T> = [T] extends [never] ? [] : K extends K ? [K, ...Permutation<Exclude<T, K>>] : never;
12+
13+
type RequiredKeys<T, K = keyof T> = K extends keyof T ? (T extends Required<Pick<T, K>> ? K : never) : never;
14+
15+
export type ExactRequiredKeys<T extends object> = Permutation<RequiredKeys<T>>;
16+
17+
export type StringifyPrimaryType<TType> = TType extends string
18+
? 'string'
19+
: TType extends number
20+
? 'number'
21+
: TType extends boolean
22+
? 'boolean'
23+
: TType extends unknown[]
24+
? 'array'
25+
: TType extends object
26+
? 'object'
27+
: never;
28+
29+
type MembersOf<U, K extends string> = K extends 'boolean'
30+
? Extract<U, boolean>
31+
: K extends 'string'
32+
? Extract<U, string>
33+
: K extends 'number'
34+
? Extract<U, number>
35+
: never;
36+
37+
type BooleanEnum = [false] | [true] | [false, true] | [true, false];
38+
39+
type EnumFor<K extends string, U> = K extends 'boolean' ? BooleanEnum : Permutation<MembersOf<U, K>>;
40+
41+
type TypeFor<K extends string, U> = {
42+
type: K;
43+
enum: EnumFor<K, U>;
44+
};
45+
46+
type Wrap<T> = { __wrapped: T };
47+
48+
type WrappedTypeFor<K extends string, U> = Wrap<TypeFor<K, U>>;
49+
50+
export type TypeGroup<U> = {
51+
[K in StringifyPrimaryType<U>]: WrappedTypeFor<K, U>;
52+
}[StringifyPrimaryType<U>];
53+
54+
export type AiFunctionCallAnyOf<U> =
55+
Permutation<TypeGroup<U>> extends infer P
56+
? P extends readonly unknown[]
57+
? { [I in keyof P]: P[I] extends Wrap<infer X> ? X : never }
58+
: never
59+
: never;

packages/ui/api/ai-assistant/types/languages.ts

Lines changed: 7 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,19 @@
1-
// Solidity
2-
import type { ERC20Options as SolidityERC20Options } from '@openzeppelin/wizard/src/erc20';
3-
import type { ERC721Options as SolidityERC721Options } from '@openzeppelin/wizard/src/erc721';
4-
import type { ERC1155Options as SolidityERC1155Options } from '@openzeppelin/wizard/src/erc1155';
5-
import type { StablecoinOptions as SolidityStablecoinOptions } from '@openzeppelin/wizard/src/stablecoin';
6-
import type { GovernorOptions as SolidityGovernorOptions } from '@openzeppelin/wizard/src/governor';
7-
import type { CustomOptions as SolidityCustomOptions } from '@openzeppelin/wizard/src/custom';
8-
import type { Access as SolidityAccesss } from '@openzeppelin/wizard/src/set-access-control';
9-
import type { Upgradeable as SolidityUpgradeable } from '@openzeppelin/wizard/src/set-upgradeable';
10-
import type { Info as SolidityInfo } from '@openzeppelin/wizard/src/set-info';
1+
import type { KindedOptions as SolidityKindedOptions } from '@openzeppelin/wizard';
2+
export type { CommonOptions as SolidityCommonOptions } from '@openzeppelin/wizard/src/common-options.ts';
113

12-
// Solidity
13-
14-
export interface SolidityCommonOptions {
15-
access?: SolidityAccesss;
16-
upgradeable?: SolidityUpgradeable;
17-
info?: SolidityInfo;
18-
}
19-
20-
export interface SolidityKindedOptions {
21-
ERC20: { kind: 'ERC20' } & SolidityCommonOptions & SolidityERC20Options;
22-
ERC721: { kind: 'ERC721' } & SolidityCommonOptions & SolidityERC721Options;
23-
ERC1155: { kind: 'ERC1155' } & SolidityCommonOptions & SolidityERC1155Options;
24-
Stablecoin: { kind: 'Stablecoin' } & SolidityCommonOptions & SolidityERC20Options & SolidityStablecoinOptions;
25-
RealWorldAsset: { kind: 'RealWorldAsset' } & SolidityCommonOptions & SolidityERC20Options & SolidityStablecoinOptions;
26-
Governor: { kind: 'Governor' } & SolidityCommonOptions & SolidityGovernorOptions;
27-
Custom: { kind: 'Custom' } & SolidityCommonOptions & SolidityCustomOptions;
28-
}
29-
30-
// After importing and building KindedOptions add supported language here
4+
// Add supported language here
315
export type LanguagesContractsOptions = {
32-
solidity: SolidityKindedOptions;
6+
solidity: Omit<SolidityKindedOptions, 'Stablecoin' | 'RealWorldAsset'> & {
7+
Stablecoin: Omit<SolidityKindedOptions['Stablecoin'], 'upgradeable'> & { upgradeable?: false };
8+
RealWorldAsset: Omit<SolidityKindedOptions['RealWorldAsset'], 'upgradeable'> & { upgradeable?: false };
9+
};
3310
};
3411

3512
export type AllLanguagesContractsOptions = LanguagesContractsOptions['solidity'];
36-
3713
//
3814

3915
export type SupportedLanguage = keyof LanguagesContractsOptions;
4016

41-
// Utils
4217
export type LanguageContractsOptions<TLanguage extends SupportedLanguage> = LanguagesContractsOptions[TLanguage];
4318

4419
export type AllLanguageContractsNames = AllLanguagesContractsOptions[keyof AllLanguagesContractsOptions]['kind'];

packages/ui/deno.json

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,7 @@
99
"dist"
1010
]
1111
},
12-
"imports": {
13-
"@openzeppelin/wizard": "./packages/wizard-solidity",
14-
"@openzeppelin/wizard-cairo": "./packages/wizard-cairo",
15-
"@openzeppelin/wizard-cairo-alpha": "./packages/wizard-cairo_alpha",
16-
"@openzeppelin/wizard-stellar": "./packages/wizard-stellar",
17-
"@openzeppelin/wizard-stylus": "./packages/wizard-stylus"
18-
},
12+
"importMap": "./import_map.json",
1913
"tasks": {
2014
"dev": "deno run --allow-all --env-file --watch --no-clear-screen scripts/development-server.ts"
2115
}

0 commit comments

Comments
 (0)