Skip to content
Merged
4 changes: 4 additions & 0 deletions packages/core-cairo/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## Unreleased

- Add Role-Based Access Control. ([#147](https://github.com/OpenZeppelin/contracts-wizard/pull/147))

## 0.3.0 (2022-07-01)

- Support Contracts for Cairo v0.2.0. ([#135](https://github.com/OpenZeppelin/contracts-wizard/pull/135))
Expand Down
4 changes: 3 additions & 1 deletion packages/core-cairo/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
},
"dependencies": {
"array.prototype.flatmap": "^1.2.4",
"bn.js": "^5.2.0"
"bn.js": "^5.2.0",
"@ethersproject/keccak256": "^5.6.1",
"@ethersproject/strings": "^5.6.1"
}
}
4 changes: 2 additions & 2 deletions packages/core-cairo/src/add-pausable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ export function addPausable(c: ContractBuilder, access: Access, pausableFns: Bas

c.addFunction(functions.paused);

requireAccessControl(c, functions.pause, access);
requireAccessControl(c, functions.unpause, access);
requireAccessControl(c, functions.pause, access, 'PAUSER');
requireAccessControl(c, functions.unpause, access, 'PAUSER');
}

const modules = defineModules( {
Expand Down
27 changes: 21 additions & 6 deletions packages/core-cairo/src/contract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export interface Contract {
constructorCode: string[];
constructorImplicitArgs?: Argument[];
constructorArgs: Argument[];
variables: string[];
upgradeable: boolean;
}

Expand All @@ -35,6 +36,7 @@ export interface BaseFunction {
name: string;
implicitArgs?: Argument[];
args: Argument[];
literalArgs?: string[];
returns?: Argument[];
returnValue?: string;
kind?: FunctionKind;
Expand All @@ -44,11 +46,16 @@ export interface BaseFunction {
}

export interface ContractFunction extends BaseFunction {
libraryCalls: BaseFunction[];
libraryCalls: LibraryCall[];
code: string[];
final: boolean;
}

export interface LibraryCall {
baseFn: BaseFunction;
literalArgs: string[];
}

export type FunctionKind = 'view' | 'external';

export interface Argument {
Expand All @@ -67,6 +74,7 @@ export class ContractBuilder implements Contract {

readonly constructorArgs: Argument[] = [];
readonly constructorCode: string[] = [];
readonly variableSet: Set<string> = new Set();

private librariesMap: Map<Module, Library> = new Map<Module, Library>();
private functionMap: Map<string, ContractFunction> = new Map();
Expand All @@ -80,6 +88,10 @@ export class ContractBuilder implements Contract {
return [...this.functionMap.values()];
}

get variables(): string[] {
return [...this.variableSet];
}

addModule(module: Module, params: Value[] = [], functions: BaseFunction[] = [], initializable: boolean = true): boolean {
const key = module;
const present = this.librariesMap.has(key);
Expand Down Expand Up @@ -116,15 +128,13 @@ export class ContractBuilder implements Contract {
}
}

addLibraryCall(callFn: BaseFunction, baseFn: BaseFunction) {
addLibraryCall(callFn: BaseFunction, baseFn: BaseFunction, literalArgs: string[] = []) {
const fn = this.addFunction(baseFn);
if (callFn.module !== undefined) {
this.addModuleFunction(callFn.module, getImportName(callFn));
}
if (callFn.args.length > 0) {
throw new Error(`Library call with functions is not supported yet`);
}
fn.libraryCalls.push(callFn);
const libraryCall: LibraryCall = { baseFn: callFn, literalArgs };
fn.libraryCalls.push(libraryCall);
}

addFunction(baseFn: BaseFunction): ContractFunction {
Expand Down Expand Up @@ -178,4 +188,9 @@ export class ContractBuilder implements Contract {
fn.final = true;
}

addVariable(code: string): boolean {
const present = this.variableSet.has(code);
this.variableSet.add(code);
return !present;
}
}
6 changes: 5 additions & 1 deletion packages/core-cairo/src/custom.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ testCustom('access control ownable', {
access: 'ownable',
});

testCustom('access control roles', {
access: 'roles',
});

testCustom('pausable with access control disabled', {
// API should override access to true since it is required for pausable
access: false,
Expand All @@ -51,7 +55,7 @@ testCustom('pausable with access control disabled', {
testAPIEquivalence('custom API default');

testAPIEquivalence('custom API full upgradeable', {
access: 'ownable',
access: 'roles',
pausable: true,
upgradeable: true,
});
Expand Down
84 changes: 84 additions & 0 deletions packages/core-cairo/src/custom.test.ts.md
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,90 @@ Generated by [AVA](https://avajs.dev).
end␊
`

## access control roles

> Snapshot 1

`# SPDX-License-Identifier: MIT␊
%lang starknet␊
from starkware.cairo.common.cairo_builtins import HashBuiltin␊
from openzeppelin.access.accesscontrol import AccessControl␊
from openzeppelin.utils.constants import DEFAULT_ADMIN_ROLE␊
@constructor␊
func constructor{␊
syscall_ptr: felt*,␊
pedersen_ptr: HashBuiltin*,␊
range_check_ptr␊
}(admin: felt):␊
AccessControl.initializer()␊
AccessControl._grant_role(DEFAULT_ADMIN_ROLE, admin)␊
return ()␊
end␊
#␊
# Getters␊
#␊
@view␊
func hasRole{␊
syscall_ptr: felt*,␊
pedersen_ptr: HashBuiltin*,␊
range_check_ptr␊
}(role: felt, user: felt) -> (has_role: felt):␊
let (has_role) = AccessControl.has_role(role, user)␊
return (has_role)␊
end␊
@view␊
func getRoleAdmin{␊
syscall_ptr: felt*,␊
pedersen_ptr: HashBuiltin*,␊
range_check_ptr␊
}(role: felt) -> (admin: felt):␊
let (admin) = AccessControl.get_role_admin(role)␊
return (admin)␊
end␊
#␊
# Externals␊
#␊
@external␊
func grantRole{␊
syscall_ptr: felt*,␊
pedersen_ptr: HashBuiltin*,␊
range_check_ptr␊
}(role: felt, user: felt):␊
AccessControl.grant_role(role, user)␊
return ()␊
end␊
@external␊
func revokeRole{␊
syscall_ptr: felt*,␊
pedersen_ptr: HashBuiltin*,␊
range_check_ptr␊
}(role: felt, user: felt):␊
AccessControl.revoke_role(role, user)␊
return ()␊
end␊
@external␊
func renounceRole{␊
syscall_ptr: felt*,␊
pedersen_ptr: HashBuiltin*,␊
range_check_ptr␊
}(role: felt, user: felt):␊
AccessControl.renounce_role(role, user)␊
return ()␊
end␊
`

## pausable with access control disabled

> Snapshot 1
Expand Down
Binary file modified packages/core-cairo/src/custom.test.ts.snap
Binary file not shown.
22 changes: 22 additions & 0 deletions packages/core-cairo/src/erc20.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ testERC20('erc20 pausable', {
access: 'ownable',
});

testERC20('erc20 pausable with roles', {
pausable: true,
access: 'roles',
});

testERC20('erc20 burnable pausable', {
burnable: true,
pausable: true,
Expand All @@ -59,9 +64,25 @@ testERC20('erc20 mintable', {
access: 'ownable',
});

testERC20('erc20 mintable with roles', {
mintable: true,
access: 'roles',
});

testERC20('erc20 full upgradeable', {
premint: '2000',
decimals: '9',
access: 'ownable',
burnable: true,
mintable: true,
pausable: true,
upgradeable: true,
});

testERC20('erc20 full upgradeable with roles', {
premint: '2000',
decimals: '9',
access: 'roles',
burnable: true,
mintable: true,
pausable: true,
Expand All @@ -77,6 +98,7 @@ testAPIEquivalence('erc20 API full upgradeable', {
symbol: 'CTK',
premint: '2000',
decimals: '9',
access: 'roles',
burnable: true,
mintable: true,
pausable: true,
Expand Down
Loading