Skip to content
Open
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
5 changes: 5 additions & 0 deletions .changeset/erc20-flash-mint.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@openzeppelin/wizard-common': patch
---

Cairo: Add ERC20FlashMint extension and AI descriptions for ERC20 token kind.
39 changes: 30 additions & 9 deletions .github/workflows/compile-cairo-alpha-project.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ jobs:
declare -a all_access_options=("disabled" "ownable" "roles" "roles-dar-default" "roles-dar-custom")
declare -a all_upgradeable_options=("true" "false")
declare -a all_royalty_options=("disabled" "enabled-default" "enabled-custom")
declare -a all_flashmint_options=("disabled" "enabled-default" "enabled-percent-fee" "enabled-custom-fee")

kind="$KIND"

Expand All @@ -78,18 +79,38 @@ jobs:
done
done

elif [[ "$kind" == "ERC20" || "$kind" == "ERC6909" || "$kind" == "Custom" ]]; then
elif [[ "$kind" == "ERC20" ]]; then
for access_option in "${all_access_options[@]}"; do
proj_name="'$kind, macros: $macros_option, access: $access_option' test project"
echo "Generating $proj_name..."
yarn run update_scarb_project --kind=$kind --macros=$macros_option --access=$access_option
for upgradeable_option in "${all_upgradeable_options[@]}"; do
for flashmint_option in "${all_flashmint_options[@]}"; do
proj_name="'$kind, macros: $macros_option, access: $access_option, upgradeable: $upgradeable_option, flashmint: $flashmint_option' test project"
echo "Generating $proj_name..."
yarn run update_scarb_project --kind=$kind --macros=$macros_option --access=$access_option --upgradeable=$upgradeable_option --flashmint=$flashmint_option

echo "Compiling $proj_name..."
scarb clean
scarb build

echo "✅ Compiled $proj_name!"
echo "---------------------------------"
done
done
done

echo "Compiling $proj_name..."
scarb clean
scarb build
elif [[ "$kind" == "ERC6909" || "$kind" == "Custom" ]]; then
for access_option in "${all_access_options[@]}"; do
for upgradeable_option in "${all_upgradeable_options[@]}"; do
proj_name="'$kind, macros: $macros_option, access: $access_option, upgradeable: $upgradeable_option' test project"
echo "Generating $proj_name..."
yarn run update_scarb_project --kind=$kind --macros=$macros_option --access=$access_option --upgradeable=$upgradeable_option

echo "✅ Compiled $proj_name!"
echo "---------------------------------"
echo "Compiling $proj_name..."
scarb clean
scarb build

echo "✅ Compiled $proj_name!"
echo "---------------------------------"
done
done

else
Expand Down
12 changes: 12 additions & 0 deletions packages/common/src/ai/descriptions/cairo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,18 @@ export const cairoERC20Descriptions = {
wrapper: 'Whether to include ERC20Wrapper functionality for depositing and withdrawing an underlying token.',
votes:
"Whether to keep track of historical balances for voting in on-chain governance, with a way to delegate one's voting power to a trusted account.",
flashmint:
'Configuration object for the ERC20FlashMint extension (ERC-3156 flash loans). The extension is included only when `enabled` is true; the other fields tune the loan limit, fee, and fee destination.',
flashMintEnabled:
'Whether to include ERC20FlashMint functionality, allowing flash loans of tokens compliant with ERC-3156.',
flashMintMaxAmount:
'Maximum amount of tokens that can be flash-loaned in a single call. Use the literal string "max" to inherit the default (the maximum representable u256 minus the current total supply), or a non-negative number in the token\'s decimal units to set a custom cap. A value of 0 effectively disables flash loans without removing the extension.',
flashMintFeeMode:
"Mode for the flash loan fee. 'percent' charges a percentage of the loaned amount (value provided via feePercent). 'custom' emits a TODO stub for the caller to implement.",
flashMintFeePercent:
'Percentage of the loan amount charged as the flash loan fee. Number between 0 and 100, fractional values supported (e.g. "0.0013725"). Used when feeMode is \'percent\'. Defaults to 0 (no fee).',
flashMintFeeDestination:
"Where the flash loan fee is sent. 'burn' sends it to the zero address (effectively burning it). 'fee_receiver' adds a constructor argument that the deployer must populate with a non-zero address; the address is stored on-chain and validated at deploy time.",
};

export const cairoERC721Descriptions = {
Expand Down
1 change: 1 addition & 0 deletions packages/core/cairo_alpha/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
- Add ERC721URIStorage extension ([#772](https://github.com/OpenZeppelin/contracts-wizard/pull/772))
- Add ERC721Wrapper extension ([#764](https://github.com/OpenZeppelin/contracts-wizard/pull/764))
- Add ERC20Wrapper extension ([#763](https://github.com/OpenZeppelin/contracts-wizard/pull/763))
- Add ERC20FlashMint extension ([#801](https://github.com/OpenZeppelin/contracts-wizard/pull/801))

- **Breaking changes**:
- Use OpenZeppelin Contracts for Cairo v4.0.0-alpha.0.
Expand Down
25 changes: 25 additions & 0 deletions packages/core/cairo_alpha/src/contract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ export interface Contract {
upgradeable: boolean;
implementedTraits: ImplementedTrait[];
superVariables: Variable[];
storageMembers: StorageMember[];
}

export interface StorageMember {
name: string;
type: string;
}

export type Value = string | number | bigint | { lit: string } | { note: string; value: Value };
Expand Down Expand Up @@ -118,6 +124,7 @@ export class ContractBuilder implements Contract {
private constantsMap: Map<string, Variable> = new Map();
private useClausesMap: Map<string, UseClause> = new Map();
private interfaceFlagsSet: Set<string> = new Set();
private storageMembersMap: Map<string, StorageMember> = new Map();

constructor(name: string, macros: MacrosOptions, account: boolean = false) {
this.name = toIdentifier(name, true);
Expand Down Expand Up @@ -145,6 +152,24 @@ export class ContractBuilder implements Contract {
return [...this.useClausesMap.values()];
}

get storageMembers(): StorageMember[] {
return [...this.storageMembersMap.values()];
}

addStorageMember(member: StorageMember): boolean {
const existing = this.storageMembersMap.get(member.name);
if (existing !== undefined) {
if (existing.type !== member.type) {
throw new Error(
`Tried to add duplicate storage member ${member.name} with different type: ${member.type} instead of ${existing.type}.`,
);
}
return false;
}
this.storageMembersMap.set(member.name, member);
return true;
}

/**
* Custom flags to denote that the contract implements a specific interface, e.g. ISRC5, to avoid duplicates
**/
Expand Down
Loading
Loading