Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: custom token registrars #116

Merged
merged 59 commits into from
Nov 1, 2023
Merged
Show file tree
Hide file tree
Changes from 39 commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
e9c1939
Added a canonical token registrar.
Foivos Oct 2, 2023
10e4503
wrote some canonical token registrar tests
Foivos Oct 2, 2023
2866ecf
made the standardized token registrar
Foivos Oct 2, 2023
1127093
Wrote some basic tests
Foivos Oct 3, 2023
943de1f
Merge remote-tracking branch 'origin/main' into feat/custom-token-reg…
Foivos Oct 3, 2023
09b66bb
lint and fix a merge bug
Foivos Oct 3, 2023
f4ef75b
feat: bump to latest gmp-sdk and cgp
milapsheth Oct 9, 2023
4569336
Merge branch 'axe-2034' into feat/custom-token-registars
milapsheth Oct 9, 2023
14fecc4
rebase over dep bump
milapsheth Oct 9, 2023
8682513
Merge branch 'main' into feat/custom-token-registars
milapsheth Oct 12, 2023
1bd5f9b
clean lock file
milapsheth Oct 12, 2023
3391cd8
Merge branch 'main' into feat/custom-token-registars
milapsheth Oct 12, 2023
f82b23c
Merge branch 'main' into feat/custom-token-registars
milapsheth Oct 12, 2023
f0a7e29
slither checks
milapsheth Oct 12, 2023
04972f9
Merge branch 'main' into feat/custom-token-registars
milapsheth Oct 12, 2023
df97235
Added roles, tests pending
Foivos Oct 17, 2023
8ea68ca
fix: tests
Oct 19, 2023
afdb98d
Merge remote-tracking branch 'origin/main' into feat/roles
Oct 19, 2023
fc0d72b
Added some tests and fixed constants
Foivos Oct 19, 2023
4b0586d
made lint happy
Foivos Oct 19, 2023
8c28d6d
Chnaged roles constants to enum to make slither happy
Foivos Oct 19, 2023
3ef1db2
Fixed tests
Foivos Oct 20, 2023
1566fec
Merge branch 'main' into feat/custom-token-registars
milapsheth Oct 23, 2023
ed4be65
querrying chain name from the remoteAddressValidator for the registrars
Foivos Oct 25, 2023
4ec3612
Merge remote-tracking branch 'origin/feat/custom-token-registars' int…
Foivos Oct 25, 2023
844bfb6
Merge remote-tracking branch 'origin/feat/roles' into feat/custom-tok…
Foivos Oct 25, 2023
a8db922
stash to add more utils to roles.
Foivos Oct 25, 2023
6c4e38f
Added getter for distributor and opearator.
Foivos Oct 25, 2023
51a3ce4
made lint happy
Foivos Oct 25, 2023
68fcbdd
fixed a spelling error
Foivos Oct 25, 2023
7b69d55
Merge branch 'feat/roles' into feat/custom-token-registars
Foivos Oct 25, 2023
7815108
Fixed tests and interfaces
Foivos Oct 25, 2023
de5bcda
Removed a lot of unused imports and made routers upgradable.
Foivos Oct 27, 2023
fe2af9f
Merge remote-tracking branch 'origin/main' into feat/roles
Foivos Oct 27, 2023
6147bf3
Merge remote-tracking branch 'origin/main' into feat/custom-token-reg…
Foivos Oct 30, 2023
09fa22d
Merge branch 'feat/roles' into feat/custom-token-registars
Foivos Oct 30, 2023
551f67e
Addressed changes
Foivos Oct 31, 2023
a6216f0
update for the new roles functions
Foivos Oct 31, 2023
992ba96
Merge branch 'feat/roles' into feat/custom-token-registars
Foivos Oct 31, 2023
05c1855
allow for mintAmount for standardized tokens
Foivos Oct 31, 2023
a593d69
Merge remote-tracking branch 'origin/main' into feat/roles
Foivos Oct 31, 2023
2831316
update sdk
Foivos Oct 31, 2023
394dca9
Merge branch 'main' into feat/roles
Foivos Oct 31, 2023
c555815
updage to newest sdk
Foivos Oct 31, 2023
fc791d1
made lint happy
Foivos Oct 31, 2023
6cc49c0
Merge branch 'feat/roles' into feat/custom-token-registars
Foivos Oct 31, 2023
b8ab648
added some changes
Foivos Oct 31, 2023
a8aac7d
Merge remote-tracking branch 'origin/main' into feat/custom-token-reg…
Foivos Oct 31, 2023
7d4d5bd
Added some utilities
Foivos Oct 31, 2023
ca65004
made lint happy
Foivos Oct 31, 2023
f190a59
Merge branch 'main' into feat/custom-token-registars
milapsheth Oct 31, 2023
9ac0cf7
fix test
milapsheth Oct 31, 2023
3b76ac1
build warnings
milapsheth Oct 31, 2023
44dc8fc
revert fix
milapsheth Nov 1, 2023
83353a6
fix create3 deployer reference
milapsheth Nov 1, 2023
e692dbc
fix test
milapsheth Nov 1, 2023
f2d76c2
slither warnings
milapsheth Nov 1, 2023
0f78048
switch proxies
milapsheth Nov 1, 2023
f7f4b47
fix import
milapsheth Nov 1, 2023
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
3 changes: 2 additions & 1 deletion .solhint.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"no-inline-assembly": "off",
"no-empty-blocks": "off",
"avoid-low-level-calls": "off",
"not-rely-on-time": "off"
"not-rely-on-time": "off",
"immutable-vars-naming": "off"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import { IRemoteAddressValidator } from '../interfaces/IRemoteAddressValidator.s
import { IInterchainTokenExecutable } from '../interfaces/IInterchainTokenExecutable.sol';
import { IInterchainTokenExpressExecutable } from '../interfaces/IInterchainTokenExpressExecutable.sol';
import { ITokenManager } from '../interfaces/ITokenManager.sol';
import { ITokenManagerProxy } from '../interfaces/ITokenManagerProxy.sol';
import { IERC20Named } from '../interfaces/IERC20Named.sol';

import { AddressBytesUtils } from '../libraries/AddressBytesUtils.sol';
Expand Down Expand Up @@ -495,7 +494,7 @@ contract InterchainTokenService is
* @param tokenIds an array of the token Ids of the tokenManagers to set the flow limit of.
* @param flowLimits the flowLimits to set
*/
function setFlowLimits(bytes32[] calldata tokenIds, uint256[] calldata flowLimits) external onlyOperator {
function setFlowLimits(bytes32[] calldata tokenIds, uint256[] calldata flowLimits) external onlyRole(uint8(Roles.OPERATOR)) {
uint256 length = tokenIds.length;
if (length != flowLimits.length) revert LengthMismatch();
for (uint256 i; i < length; ++i) {
Expand All @@ -518,7 +517,7 @@ contract InterchainTokenService is
\****************/

function _setup(bytes calldata params) internal override {
_setOperator(params.toAddress());
_addOperator(params.toAddress());
}

function _sanitizeTokenManagerImplementation(
Expand Down
17 changes: 17 additions & 0 deletions contracts/interfaces/ICanonicalTokenRegistrar.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

interface ICanonicalTokenRegistrar {
error ZeroAddress();

function chainNameHash() external view returns (bytes32);

function getCanonicalTokenSalt(address tokenAddress) external view returns (bytes32 salt);

function getCanonicalTokenId(address tokenAddress) external view returns (bytes32 tokenId);
Comment on lines +11 to +13
Copy link
Member

@milapsheth milapsheth Oct 31, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove get prefix from registrar contracts as well (along with ITS)


function registerCanonicalToken(address tokenAddress) external payable returns (bytes32 tokenId);

function deployAndRegisterRemoteCanonicalToken(bytes32 salt, string calldata destinationChain, uint256 gasValue) external payable;
}
22 changes: 9 additions & 13 deletions contracts/interfaces/IDistributable.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,9 @@

pragma solidity ^0.8.0;

interface IDistributable {
error NotDistributor();
error NotProposedDistributor();

event DistributorshipTransferred(address indexed distributor);
event DistributorshipTransferStarted(address indexed distributor);

/**
* @notice Get the address of the distributor
* @return distributor_ of the distributor
*/
function distributor() external view returns (address distributor_);
import { IRolesBase } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IRolesBase.sol';

interface IDistributable is IRolesBase {
/**
* @notice Change the distributor of the contract
* @dev Can only be called by the current distributor
Expand All @@ -33,5 +23,11 @@ interface IDistributable {
* @notice Accept a change of the distributor of the contract
* @dev Can only be called by the proposed distributor
*/
function acceptDistributorship() external;
function acceptDistributorship(address fromDistributor) external;

/**
* @notice Query if an address is a distributor
* @param addr the address to query for
*/
function isDistributor(address addr) external view returns (bool);
}
7 changes: 7 additions & 0 deletions contracts/interfaces/IInterchainTokenService.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { IExpressCallHandler } from './IExpressCallHandler.sol';
import { ITokenManagerType } from './ITokenManagerType.sol';
import { IPausable } from './IPausable.sol';
import { IMulticall } from './IMulticall.sol';
import { IRemoteAddressValidator } from './IRemoteAddressValidator.sol';

interface IInterchainTokenService is ITokenManagerType, IExpressCallHandler, IAxelarExecutable, IPausable, IMulticall, IContractIdentifier {
error ZeroAddress();
Expand Down Expand Up @@ -78,6 +79,12 @@ interface IInterchainTokenService is ITokenManagerType, IExpressCallHandler, IAx
);
event CustomTokenIdClaimed(bytes32 indexed tokenId, address indexed deployer, bytes32 indexed salt);

/**
* @notice Returns the address of the token manager deployer contract.
* @return remoteAddressValidator The remoteAddressValidator.
*/
function remoteAddressValidator() external view returns (IRemoteAddressValidator remoteAddressValidator);

/**
* @notice Returns the address of the token manager deployer contract.
* @return tokenManagerDeployerAddress The address of the token manager deployer contract.
Expand Down
22 changes: 9 additions & 13 deletions contracts/interfaces/IOperatable.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,9 @@

pragma solidity ^0.8.0;

interface IOperatable {
error NotOperator();
error NotProposedOperator();

event OperatorshipTransferred(address indexed operator);
event OperatorChangeProposed(address indexed operator);

/**
* @notice Get the address of the operator
* @return operator_ of the operator
*/
function operator() external view returns (address operator_);
import { IRolesBase } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IRolesBase.sol';

interface IOperatable is IRolesBase {
/**
* @notice Change the operator of the contract
* @dev Can only be called by the current operator
Expand All @@ -33,5 +23,11 @@ interface IOperatable {
* @notice Accept a proposed change of operatorship
* @dev Can only be called by the proposed operator
*/
function acceptOperatorship() external;
function acceptOperatorship(address fromOperator) external;

/**
* @notice Query if an address is a operator
* @param addr the address to query for
*/
function isOperator(address addr) external view returns (bool);
}
20 changes: 17 additions & 3 deletions contracts/interfaces/IStandardizedToken.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,34 @@

pragma solidity ^0.8.0;

import { IERC20 } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IERC20.sol';
import { IContractIdentifier } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IContractIdentifier.sol';

import { IImplementation } from './IImplementation.sol';
import { IInterchainToken } from './IInterchainToken.sol';
import { IDistributable } from './IDistributable.sol';
import { IERC20MintableBurnable } from './IERC20MintableBurnable.sol';
import { ITokenManager } from './ITokenManager.sol';
import { IERC20Named } from './IERC20Named.sol';

/**
* @title StandardizedToken
* @notice This contract implements a standardized token which extends InterchainToken functionality.
* This contract also inherits Distributable and Implementation logic.
*/
interface IStandardizedToken is IImplementation, IInterchainToken, IDistributable, IERC20MintableBurnable, IERC20, IContractIdentifier {
interface IStandardizedToken is IInterchainToken, IDistributable, IERC20MintableBurnable, IERC20Named, IContractIdentifier {
error TokenManagerAddressZero();
error TokenNameEmpty();

/**
* @notice Called by the proxy to setup itself.
* @dev This should be hidden by the proxy.
* @param params the data to be used for the initialization.
*/
function setup(bytes calldata params) external;

/**
* @notice Getter for the tokenManager used for this token.
* @dev Needs to be overwitten.
* @return tokenManager_ the TokenManager called to facilitate cross chain transfers.
*/
function tokenManager() external view returns (ITokenManager tokenManager_);
}
2 changes: 0 additions & 2 deletions contracts/interfaces/IStandardizedTokenDeployer.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

pragma solidity ^0.8.0;

import { Create3Deployer } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/deploy/Create3Deployer.sol';

/**
* @title IStandardizedTokenDeployer
* @notice This contract is used to deploy new instances of the StandardizedTokenProxy contract.
Expand Down
34 changes: 34 additions & 0 deletions contracts/interfaces/IStandardizedTokenRegistrar.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

interface IStandardizedTokenRegistrar {
error ZeroAddress();
error NotDistributor(address distributor);
error NotOperator(address operator);

function chainNameHash() external view returns (bytes32);

function getStandardizedTokenSalt(address deployer, bytes32 salt) external view returns (bytes32);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same get comment


function getStandardizedTokenId(address deployer, bytes32 salt) external view returns (bytes32 tokenId);

function getStandardizedTokenAddress(address deployer, bytes32 salt) external view returns (address tokenAddress);

function deployStandardizedToken(
bytes32 salt,
string calldata name,
string calldata symbol,
uint8 decimals,
uint256 mintAmount,
address distributor
) external payable;

function deployRemoteStandarizedToken(
bytes32 salt,
address additionalDistributor,
address optionalOperator,
string memory destinationChain,
uint256 gasValue
) external payable;
}
3 changes: 3 additions & 0 deletions contracts/interfaces/ITokenManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ interface ITokenManager is ITokenManagerType, IOperatable, IFlowLimit, IImplemen
error TakeTokenFailed();
error GiveTokenFailed();
error NotToken();
error ZeroAddress();
error AlreadyFlowLimiter(address flowLimiter);
error NotFlowLimiter(address flowLimiter);

/**
* @notice A function that returns the token id.
Expand Down
2 changes: 0 additions & 2 deletions contracts/interfaces/ITokenManagerDeployer.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

pragma solidity ^0.8.0;

import { Create3Deployer } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/deploy/Create3Deployer.sol';

/**
* @title ITokenManagerDeployer
* @notice This contract is used to deploy new instances of the TokenManagerProxy contract.
Expand Down
28 changes: 28 additions & 0 deletions contracts/proxies/CanonicalTokenRegistrarProxy.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import { FinalProxy } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/upgradable/FinalProxy.sol';

/**
* @title InterchainTokenServiceProxy
* @dev Proxy contract for interchain token service contracts. Inherits from the FinalProxy contract.
*/
contract CanonicalTokenRegistrarProxy is FinalProxy {
bytes32 private constant CONTRACT_ID = keccak256('canonical-token-registrar');

/**
* @dev Constructs the InterchainTokenServiceProxy contract.
* @param implementationAddress Address of the interchain token service implementation
* @param owner Address of the owner of the proxy
*/
constructor(address implementationAddress, address owner) FinalProxy(implementationAddress, owner, '') {}

/**
* @dev Override for the 'contractId' function in FinalProxy. Returns a unique identifier for this contract.
* @return bytes32 identifier for this contract
*/
function contractId() internal pure override returns (bytes32) {
return CONTRACT_ID;
}
}
28 changes: 28 additions & 0 deletions contracts/proxies/StandardizedTokenRegistrarProxy.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import { FinalProxy } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/upgradable/FinalProxy.sol';

/**
* @title InterchainTokenServiceProxy
* @dev Proxy contract for interchain token service contracts. Inherits from the FinalProxy contract.
*/
contract StandardizedTokenRegistrarProxy is FinalProxy {
bytes32 private constant CONTRACT_ID = keccak256('standardized-token-registrar');

/**
* @dev Constructs the InterchainTokenServiceProxy contract.
* @param implementationAddress Address of the interchain token service implementation
* @param owner Address of the owner of the proxy
*/
constructor(address implementationAddress, address owner) FinalProxy(implementationAddress, owner, '') {}
milapsheth marked this conversation as resolved.
Show resolved Hide resolved

/**
* @dev Override for the 'contractId' function in FinalProxy. Returns a unique identifier for this contract.
* @return bytes32 identifier for this contract
*/
function contractId() internal pure override returns (bytes32) {
return CONTRACT_ID;
}
}
6 changes: 3 additions & 3 deletions contracts/test/FeeOnTransferTokenTest.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ contract FeeOnTransferTokenTest is InterchainToken, Distributable, IERC20Mintabl
name = name_;
symbol = symbol_;
decimals = decimals_;
_setDistributor(msg.sender);
_addDistributor(msg.sender);
tokenManager_ = ITokenManager(tokenManagerAddress);
}

Expand Down Expand Up @@ -50,11 +50,11 @@ contract FeeOnTransferTokenTest is InterchainToken, Distributable, IERC20Mintabl
tokenManagerRequiresApproval_ = requiresApproval;
}

function mint(address account, uint256 amount) external onlyDistributor {
function mint(address account, uint256 amount) external onlyRole(uint8(Roles.DISTRIBUTOR)) {
_mint(account, amount);
}

function burn(address account, uint256 amount) external onlyDistributor {
function burn(address account, uint256 amount) external onlyRole(uint8(Roles.DISTRIBUTOR)) {
_burn(account, amount);
}

Expand Down
10 changes: 2 additions & 8 deletions contracts/test/HardCodedConstantsTest.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

Check warning on line 3 in contracts/test/HardCodedConstantsTest.sol

View workflow job for this annotation

GitHub Actions / lint (18.x, ubuntu-latest)

Found more than One contract per file. 6 contracts found!

import { TokenManagerLiquidityPool } from '../token-manager/implementations/TokenManagerLiquidityPool.sol';
import { Distributable } from '../utils/Distributable.sol';
Expand All @@ -13,25 +13,22 @@
string public constant NAME = 'TestTokenManager';

constructor(address interchainTokenService_) TokenManagerLiquidityPool(interchainTokenService_) {
require(TOKEN_ADDRESS_SLOT == uint256(keccak256('token-address')) - 1, 'invalid constant');

Check warning on line 16 in contracts/test/HardCodedConstantsTest.sol

View workflow job for this annotation

GitHub Actions / lint (18.x, ubuntu-latest)

Use Custom Errors instead of require statements
require(LIQUIDITY_POOL_SLOT == uint256(keccak256('liquidity-pool-slot')) - 1, 'invalid constant');

Check warning on line 17 in contracts/test/HardCodedConstantsTest.sol

View workflow job for this annotation

GitHub Actions / lint (18.x, ubuntu-latest)

Use Custom Errors instead of require statements
}
}

contract TestDistributable is Distributable {
string public constant NAME = 'TestDistributable';

constructor() {
require(DISTRIBUTOR_SLOT == uint256(keccak256('distributor')) - 1, 'invalid constant');
require(PROPOSED_DISTRIBUTOR_SLOT == uint256(keccak256('proposed-distributor')) - 1, 'invalid constant');
}
constructor() {}
}

contract TestFlowLimit is FlowLimit {
string public constant NAME = 'TestFlowLimit';

constructor() {
require(FLOW_LIMIT_SLOT == uint256(keccak256('flow-limit')) - 1, 'invalid constant');

Check warning on line 31 in contracts/test/HardCodedConstantsTest.sol

View workflow job for this annotation

GitHub Actions / lint (18.x, ubuntu-latest)

Use Custom Errors instead of require statements
}
}

Expand All @@ -39,23 +36,20 @@
string public constant NAME = 'TestNoReEntrancy';

constructor() {
require(ENTERED_SLOT == uint256(keccak256('entered')) - 1, 'invalid constant');

Check warning on line 39 in contracts/test/HardCodedConstantsTest.sol

View workflow job for this annotation

GitHub Actions / lint (18.x, ubuntu-latest)

Use Custom Errors instead of require statements
}
}

contract TestOperatable is Operatable {
string public constant NAME = 'TestOperatable';

constructor() {
require(OPERATOR_SLOT == uint256(keccak256('operator')) - 1, 'invalid constant');
require(PROPOSED_OPERATOR_SLOT == uint256(keccak256('proposed-operator')) - 1, 'invalid constant');
}
constructor() {}
}

contract TestPausable is Pausable {
string public constant NAME = 'TestPausable';

constructor() {
require(PAUSE_SLOT == uint256(keccak256('paused')) - 1, 'invalid constant');

Check warning on line 53 in contracts/test/HardCodedConstantsTest.sol

View workflow job for this annotation

GitHub Actions / lint (18.x, ubuntu-latest)

Use Custom Errors instead of require statements
}
}
1 change: 0 additions & 1 deletion contracts/test/InterchainExecutableTest.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
pragma solidity ^0.8.0;

import { InterchainTokenExpressExecutable } from '../examples/InterchainTokenExpressExecutable.sol';
import { IInterchainTokenService } from '../interfaces/IInterchainTokenService.sol';
import { IERC20 } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IERC20.sol';

contract InterchainExecutableTest is InterchainTokenExpressExecutable {
Expand Down
6 changes: 3 additions & 3 deletions contracts/test/InterchainTokenTest.sol
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ contract InterchainTokenTest is InterchainToken, Distributable, IERC20MintableBu
name = name_;
symbol = symbol_;
decimals = decimals_;
_setDistributor(msg.sender);
_addDistributor(msg.sender);
tokenManager_ = ITokenManager(tokenManagerAddress);
}

Expand Down Expand Up @@ -49,11 +49,11 @@ contract InterchainTokenTest is InterchainToken, Distributable, IERC20MintableBu
tokenManagerRequiresApproval_ = requiresApproval;
}

function mint(address account, uint256 amount) external onlyDistributor {
function mint(address account, uint256 amount) external onlyRole(uint8(Roles.DISTRIBUTOR)) {
_mint(account, amount);
}

function burn(address account, uint256 amount) external onlyDistributor {
function burn(address account, uint256 amount) external onlyRole(uint8(Roles.DISTRIBUTOR)) {
_burn(account, amount);
}

Expand Down
Loading
Loading