mirror of
https://github.com/luxfi/erc-3643.git
synced 2026-10-30 16:58:28 +00:00
## [4.1.0]
### Breaking Changes
- **TREXFactory Constructor**: Now requires the address of the Identity Factory.
- Reason: The Identity Factory is used to deploy ONCHAINIDs for tokens.
### Added
- **Compliance Modules**:
- Introduced `Supply Limit Module`: Restricts minting tokens beyond a specified limit.
- Introduced `Time Transfers Limits`: Prevents holders from transferring tokens beyond a set limit within a specified timeframe.
- Introduced `Max Balance Module`: Ensures an individual holder doesn't exceed a certain percentage of the total supply.
- Added two exchange-specific modules:
- `Time Exchange Limits`: Limits token transfers on trusted exchanges within a set timeframe.
- `Monthly Exchange Limits`: Restricts the amount of tokens that can be transferred on trusted exchanges each month.
- Introduced `Transfer Fees` : Collects fees from transfers (issuers determine fee rates).
- **IModule Enhancement**:
- Added a new function: `function name() external pure returns (string memory _name);`. This mandates all compliance modules to declare a constant variable, e.g., `function name() public pure returns (string memory _name) { return "CountryRestrictModule"; }`.
- New function `isPlugAndPlay`: Added to the `IModule` interface. This function, `function isPlugAndPlay() external pure returns (bool)`, indicates whether a compliance module can be bound without presetting. It is now mandatory for all compliance modules to declare this function.
- New function `canComplianceBind`: Also added to the `IModule` interface. Compliance modules must implement `function canComplianceBind(address _compliance) external view returns (bool)`, which checks if presetting is required before binding a compliance module.
- **TREXFactory Enhancements**:
- New function `setIdFactory`: Sets the Identity Factory responsible for deploying token ONCHAINIDs.
- New function `getIdFactory`: Retrieves the address of the associated Identity Factory.
- **TREXGateway Contract**:
- Deployed as a central interface for the TREX ecosystem, facilitating various crucial operations:
- **Factory Management**: Manages the Factory contract address, enabling updates and ownership transfers.
- **Public Deployment Control**: Toggles the ability for public entities to deploy TREX contracts, enhancing security and flexibility.
- **Fee Management**: Sets and adjusts deployment fee details, including amount, token type, and collector address, and enables or disables fee requirements.
- **Deployer Management**: Adds or removes approved deployers and applies fee discounts, including batch operations for efficiency. Ensures streamlined deployment processes for TREX contracts.
- **Suite Deployment**: Directly deploys TREX suites of contracts using provided token and claim details, with support for batch deployments. Incorporates fee collection and deployment status checks for each deployment, emphasizing security and compliance.
- **Status and Fee Queries**: Provides functions to retrieve current public deployment status, Factory contract address, deployment fee details, and deployment fee status.
- **Fee Calculation**: Dynamically calculates deployment fees for deployers, considering applicable discounts.
- **DvATransferManager Contract**:
- Introduced the `DvATransferManager` contract to streamline the process of internal fund transfers needing multi-party intermediate approvals.
- Token owners define the transfer authorization criteria, including recipient approval, agent approval, and potential additional approvers.
- Investors submit transfer requests.
- Approvers are empowered to either sanction or reject these requests.
- Transfers are executed only upon receiving unanimous approval from all designated approvers.
### Updates
#### Smart Contract Enhancements
- **TREXFactory**:
- Modified the `deployTREXSuite` function to now auto-deploy a Token ONCHAINID if it's not already available (i.e., if the onchainid address in _tokenDetails is the zero address).
- **ModularCompliance**:
- Updated the `addModule` function to invoke the new `isPlugAndPlay` and `canComplianceBind` functions, ensuring compatibility checks before binding any compliance module.
#### Code Quality Improvements
- Enhanced the GitHub Actions workflow by adding TypeScript linting (`lint:ts`) for test files, ensuring higher code quality and adherence to coding standards.
- Executed a comprehensive linting pass on all test files, addressing and resolving any linting issues. This ensures a consistent code style and improved readability across the test suite.
- Updated the `push_checking.yml` GitHub Actions workflow to include automatic TypeScript linting checks on pull requests. This addition enforces coding standards and helps maintain high-quality code submissions from all contributors.
---------
Co-authored-by: Mihalis Plelis <[email protected]>
Co-authored-by: Nakasar <[email protected]>
Co-authored-by: aliarbak <[email protected]>
573 lines
25 KiB
TypeScript
573 lines
25 KiB
TypeScript
import { loadFixture } from '@nomicfoundation/hardhat-network-helpers';
|
|
import { expect } from 'chai';
|
|
import { ethers } from 'hardhat';
|
|
|
|
import { deployFullSuiteFixture } from '../fixtures/deploy-full-suite.fixture';
|
|
|
|
describe('TrexImplementationAuthority', () => {
|
|
describe('.setTREXFactory()', () => {
|
|
describe('When not called by the owner', () => {
|
|
it('Should revert', async () => {
|
|
const {
|
|
authorities: { trexImplementationAuthority },
|
|
accounts: { anotherWallet },
|
|
} = await loadFixture(deployFullSuiteFixture);
|
|
|
|
await expect(trexImplementationAuthority.connect(anotherWallet).setTREXFactory(ethers.constants.AddressZero)).to.be.revertedWith(
|
|
'Ownable: caller is not the owner',
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('When called by the owner', () => {
|
|
describe('When the authority has reference status true', () => {
|
|
describe('When the trex factory to add is not using this authority contract', () => {
|
|
it('Should revert for invalid link between the factory and the authority', async () => {
|
|
const {
|
|
accounts: { deployer },
|
|
authorities: { trexImplementationAuthority },
|
|
factories: { identityFactory },
|
|
implementations,
|
|
} = await loadFixture(deployFullSuiteFixture);
|
|
|
|
const otherTrexImplementationAuthority = await ethers.deployContract(
|
|
'TREXImplementationAuthority',
|
|
[true, ethers.constants.AddressZero, ethers.constants.AddressZero],
|
|
deployer,
|
|
);
|
|
const versionStruct = {
|
|
major: 4,
|
|
minor: 0,
|
|
patch: 0,
|
|
};
|
|
const contractsStruct = {
|
|
tokenImplementation: implementations.tokenImplementation.address,
|
|
ctrImplementation: implementations.claimTopicsRegistryImplementation.address,
|
|
irImplementation: implementations.identityRegistryImplementation.address,
|
|
irsImplementation: implementations.identityRegistryStorageImplementation.address,
|
|
tirImplementation: implementations.trustedIssuersRegistryImplementation.address,
|
|
mcImplementation: implementations.modularComplianceImplementation.address,
|
|
};
|
|
await otherTrexImplementationAuthority.connect(deployer).addAndUseTREXVersion(versionStruct, contractsStruct);
|
|
|
|
const trexFactory = await ethers.deployContract(
|
|
'TREXFactory',
|
|
[otherTrexImplementationAuthority.address, identityFactory.address],
|
|
deployer,
|
|
);
|
|
|
|
await expect(trexImplementationAuthority.setTREXFactory(trexFactory.address)).to.be.revertedWith('only reference contract can call');
|
|
});
|
|
});
|
|
|
|
describe('When the trex factory to add is using this authority contract', () => {
|
|
it('should set the trex factory address', async () => {
|
|
const {
|
|
accounts: { deployer },
|
|
authorities: { trexImplementationAuthority },
|
|
factories: { identityFactory },
|
|
} = await loadFixture(deployFullSuiteFixture);
|
|
|
|
const trexFactory = await ethers.deployContract('TREXFactory', [trexImplementationAuthority.address, identityFactory.address], deployer);
|
|
|
|
const tx = await trexImplementationAuthority.setTREXFactory(trexFactory.address);
|
|
await expect(tx).to.emit(trexImplementationAuthority, 'TREXFactorySet').withArgs(trexFactory.address);
|
|
await expect(trexImplementationAuthority.getTREXFactory()).to.eventually.equal(trexFactory.address);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('.setIAFactory()', () => {
|
|
describe('When not called by the owner', () => {
|
|
it('Should revert', async () => {
|
|
const {
|
|
authorities: { trexImplementationAuthority },
|
|
accounts: { anotherWallet },
|
|
} = await loadFixture(deployFullSuiteFixture);
|
|
|
|
await expect(trexImplementationAuthority.connect(anotherWallet).setIAFactory(ethers.constants.AddressZero)).to.be.revertedWith(
|
|
'Ownable: caller is not the owner',
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('When called by the owner', () => {
|
|
describe('When the authority has reference status true', () => {
|
|
describe('When the trex factory to add is using this authority contract', () => {
|
|
it('should set the trex factory address', async () => {
|
|
const {
|
|
accounts: { deployer },
|
|
authorities: { trexImplementationAuthority },
|
|
factories: { identityFactory },
|
|
} = await loadFixture(deployFullSuiteFixture);
|
|
|
|
const trexFactory = await ethers.deployContract('TREXFactory', [trexImplementationAuthority.address, identityFactory.address], deployer);
|
|
await trexImplementationAuthority.setTREXFactory(trexFactory.address);
|
|
|
|
const implementationAuthorityFactory = await ethers.deployContract(
|
|
'TREXImplementationAuthority',
|
|
[true, ethers.constants.AddressZero, ethers.constants.AddressZero],
|
|
deployer,
|
|
);
|
|
|
|
const tx = await trexImplementationAuthority.setIAFactory(implementationAuthorityFactory.address);
|
|
await expect(tx).to.emit(trexImplementationAuthority, 'IAFactorySet').withArgs(implementationAuthorityFactory.address);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('.fetchVersion()', () => {
|
|
describe('when called on the reference contract', () => {
|
|
it('should revert because the reference contract cannot fetch its own versions', async () => {
|
|
const {
|
|
authorities: { trexImplementationAuthority },
|
|
} = await loadFixture(deployFullSuiteFixture);
|
|
|
|
const versionStruct = {
|
|
major: 4,
|
|
minor: 0,
|
|
patch: 0,
|
|
};
|
|
|
|
await expect(trexImplementationAuthority.fetchVersion(versionStruct)).to.be.revertedWith('cannot call on reference contract');
|
|
});
|
|
});
|
|
|
|
describe('when version were already fetched', () => {
|
|
it('should revert', async () => {
|
|
const {
|
|
accounts: { deployer },
|
|
authorities: { trexImplementationAuthority },
|
|
factories: { identityFactory },
|
|
} = await loadFixture(deployFullSuiteFixture);
|
|
|
|
const trexFactory = await ethers.deployContract('TREXFactory', [trexImplementationAuthority.address, identityFactory.address], deployer);
|
|
|
|
const otherTrexImplementationAuthority = await ethers.deployContract(
|
|
'TREXImplementationAuthority',
|
|
[false, trexFactory.address, trexImplementationAuthority.address],
|
|
deployer,
|
|
);
|
|
|
|
const versionStruct = {
|
|
major: 4,
|
|
minor: 0,
|
|
patch: 0,
|
|
};
|
|
|
|
await otherTrexImplementationAuthority.fetchVersion(versionStruct);
|
|
|
|
await expect(otherTrexImplementationAuthority.fetchVersion(versionStruct)).to.be.revertedWith('version fetched already');
|
|
});
|
|
});
|
|
|
|
describe('when version should be setup', () => {
|
|
it('should fetch and set the versions of the implementation from the reference contract', async () => {
|
|
const {
|
|
accounts: { deployer },
|
|
authorities: { trexImplementationAuthority },
|
|
factories: { identityFactory },
|
|
} = await loadFixture(deployFullSuiteFixture);
|
|
|
|
const trexFactory = await ethers.deployContract('TREXFactory', [trexImplementationAuthority.address, identityFactory.address], deployer);
|
|
|
|
const otherTrexImplementationAuthority = await ethers.deployContract(
|
|
'TREXImplementationAuthority',
|
|
[false, trexFactory.address, trexImplementationAuthority.address],
|
|
deployer,
|
|
);
|
|
|
|
const versionStruct = {
|
|
major: 4,
|
|
minor: 0,
|
|
patch: 0,
|
|
};
|
|
|
|
const tx = await otherTrexImplementationAuthority.fetchVersion(versionStruct);
|
|
expect(tx).to.emit(otherTrexImplementationAuthority, 'TREXVersionFetched');
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('.addTREXVersion()', () => {
|
|
describe('when called not as owner', () => {
|
|
it('should revert', async () => {
|
|
const {
|
|
accounts: { anotherWallet },
|
|
authorities: { trexImplementationAuthority },
|
|
implementations,
|
|
} = await loadFixture(deployFullSuiteFixture);
|
|
|
|
const versionStruct = {
|
|
major: 4,
|
|
minor: 0,
|
|
patch: 1,
|
|
};
|
|
const contractsStruct = {
|
|
tokenImplementation: implementations.tokenImplementation.address,
|
|
ctrImplementation: implementations.claimTopicsRegistryImplementation.address,
|
|
irImplementation: implementations.identityRegistryImplementation.address,
|
|
irsImplementation: ethers.constants.AddressZero,
|
|
tirImplementation: implementations.trustedIssuersRegistryImplementation.address,
|
|
mcImplementation: implementations.modularComplianceImplementation.address,
|
|
};
|
|
|
|
await expect(trexImplementationAuthority.connect(anotherWallet).addTREXVersion(versionStruct, contractsStruct)).to.be.revertedWith(
|
|
'Ownable: caller is not the owner',
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('when called as owner', () => {
|
|
describe('when called on a non-reference contract', () => {
|
|
it('should revert', async () => {
|
|
const {
|
|
accounts: { deployer },
|
|
authorities: { trexImplementationAuthority },
|
|
factories: { identityFactory },
|
|
implementations,
|
|
} = await loadFixture(deployFullSuiteFixture);
|
|
|
|
const trexFactory = await ethers.deployContract('TREXFactory', [trexImplementationAuthority.address, identityFactory.address], deployer);
|
|
|
|
const otherTrexImplementationAuthority = await ethers.deployContract(
|
|
'TREXImplementationAuthority',
|
|
[false, trexFactory.address, trexImplementationAuthority.address],
|
|
deployer,
|
|
);
|
|
|
|
const versionStruct = {
|
|
major: 4,
|
|
minor: 0,
|
|
patch: 0,
|
|
};
|
|
const contractsStruct = {
|
|
tokenImplementation: implementations.tokenImplementation.address,
|
|
ctrImplementation: implementations.claimTopicsRegistryImplementation.address,
|
|
irImplementation: implementations.identityRegistryImplementation.address,
|
|
irsImplementation: implementations.identityRegistryStorageImplementation.address,
|
|
tirImplementation: implementations.trustedIssuersRegistryImplementation.address,
|
|
mcImplementation: implementations.modularComplianceImplementation.address,
|
|
};
|
|
|
|
await expect(otherTrexImplementationAuthority.addTREXVersion(versionStruct, contractsStruct)).to.be.revertedWith(
|
|
'ONLY reference contract can add versions',
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('when called on a reference contract', () => {
|
|
describe('when version were already added', () => {
|
|
it('should revert', async () => {
|
|
const {
|
|
authorities: { trexImplementationAuthority },
|
|
implementations,
|
|
} = await loadFixture(deployFullSuiteFixture);
|
|
|
|
const versionStruct = {
|
|
major: 4,
|
|
minor: 0,
|
|
patch: 0,
|
|
};
|
|
const contractsStruct = {
|
|
tokenImplementation: implementations.tokenImplementation.address,
|
|
ctrImplementation: implementations.claimTopicsRegistryImplementation.address,
|
|
irImplementation: implementations.identityRegistryImplementation.address,
|
|
irsImplementation: implementations.identityRegistryStorageImplementation.address,
|
|
tirImplementation: implementations.trustedIssuersRegistryImplementation.address,
|
|
mcImplementation: implementations.modularComplianceImplementation.address,
|
|
};
|
|
|
|
await expect(trexImplementationAuthority.addTREXVersion(versionStruct, contractsStruct)).to.be.revertedWith('version already exists');
|
|
});
|
|
});
|
|
|
|
describe('when a contract implementation address is the zero address', () => {
|
|
it('should revert', async () => {
|
|
const {
|
|
authorities: { trexImplementationAuthority },
|
|
implementations,
|
|
} = await loadFixture(deployFullSuiteFixture);
|
|
|
|
const versionStruct = {
|
|
major: 4,
|
|
minor: 0,
|
|
patch: 1,
|
|
};
|
|
const contractsStruct = {
|
|
tokenImplementation: implementations.tokenImplementation.address,
|
|
ctrImplementation: implementations.claimTopicsRegistryImplementation.address,
|
|
irImplementation: implementations.identityRegistryImplementation.address,
|
|
irsImplementation: ethers.constants.AddressZero,
|
|
tirImplementation: implementations.trustedIssuersRegistryImplementation.address,
|
|
mcImplementation: implementations.modularComplianceImplementation.address,
|
|
};
|
|
|
|
await expect(trexImplementationAuthority.addTREXVersion(versionStruct, contractsStruct)).to.be.revertedWith(
|
|
'invalid argument - zero address',
|
|
);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('.useTREXVersion()', () => {
|
|
describe('when called not as owner', () => {
|
|
it('should revert', async () => {
|
|
const {
|
|
accounts: { anotherWallet },
|
|
authorities: { trexImplementationAuthority },
|
|
} = await loadFixture(deployFullSuiteFixture);
|
|
|
|
const versionStruct = {
|
|
major: 4,
|
|
minor: 0,
|
|
patch: 0,
|
|
};
|
|
|
|
await expect(trexImplementationAuthority.connect(anotherWallet).useTREXVersion(versionStruct)).to.be.revertedWith(
|
|
'Ownable: caller is not the owner',
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('when called as owner', () => {
|
|
describe('when version is already in use', () => {
|
|
it('should revert', async () => {
|
|
const {
|
|
authorities: { trexImplementationAuthority },
|
|
} = await loadFixture(deployFullSuiteFixture);
|
|
|
|
const versionStruct = {
|
|
major: 4,
|
|
minor: 0,
|
|
patch: 0,
|
|
};
|
|
|
|
await expect(trexImplementationAuthority.useTREXVersion(versionStruct)).to.be.revertedWith('version already in use');
|
|
});
|
|
});
|
|
|
|
describe('when version does not exist', () => {
|
|
it('should revert', async () => {
|
|
const {
|
|
authorities: { trexImplementationAuthority },
|
|
} = await loadFixture(deployFullSuiteFixture);
|
|
|
|
const versionStruct = {
|
|
major: 4,
|
|
minor: 0,
|
|
patch: 1,
|
|
};
|
|
|
|
await expect(trexImplementationAuthority.useTREXVersion(versionStruct)).to.be.revertedWith('invalid argument - non existing version');
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('.changeImplementationAuthority()', () => {
|
|
describe('when token to update is the zero address', () => {
|
|
it('should revert', async () => {
|
|
const {
|
|
accounts: { anotherWallet },
|
|
authorities: { trexImplementationAuthority },
|
|
} = await loadFixture(deployFullSuiteFixture);
|
|
|
|
await expect(
|
|
trexImplementationAuthority.changeImplementationAuthority(ethers.constants.AddressZero, anotherWallet.address),
|
|
).to.be.revertedWith('invalid argument - zero address');
|
|
});
|
|
});
|
|
|
|
describe('whe new authority is the zero address', () => {
|
|
describe('when called on a non-reference authority contract', () => {
|
|
it('should revert', async () => {
|
|
const {
|
|
accounts: { deployer },
|
|
authorities: { trexImplementationAuthority },
|
|
factories: { identityFactory },
|
|
suite: { token },
|
|
} = await loadFixture(deployFullSuiteFixture);
|
|
|
|
const trexFactory = await ethers.deployContract('TREXFactory', [trexImplementationAuthority.address, identityFactory.address], deployer);
|
|
|
|
const otherTrexImplementationAuthority = await ethers.deployContract(
|
|
'TREXImplementationAuthority',
|
|
[false, trexFactory.address, trexImplementationAuthority.address],
|
|
deployer,
|
|
);
|
|
|
|
await expect(
|
|
otherTrexImplementationAuthority.changeImplementationAuthority(token.address, ethers.constants.AddressZero),
|
|
).to.be.revertedWith('only reference contract can deploy new IAs');
|
|
});
|
|
});
|
|
|
|
describe('when called on a reference authority contract', () => {
|
|
describe('when caller is not owner of the token (or any contract of the suite)', () => {
|
|
it('should revert', async () => {
|
|
const {
|
|
accounts: { anotherWallet },
|
|
authorities: { trexImplementationAuthority },
|
|
suite: { token },
|
|
} = await loadFixture(deployFullSuiteFixture);
|
|
|
|
await expect(
|
|
trexImplementationAuthority.connect(anotherWallet).changeImplementationAuthority(token.address, ethers.constants.AddressZero),
|
|
).to.be.revertedWith('caller NOT owner of all contracts impacted');
|
|
});
|
|
});
|
|
|
|
describe('when caller is owner of every contract of the suite of the token', () => {
|
|
it('should deploy a new authority contract', async () => {
|
|
const {
|
|
accounts: { deployer },
|
|
authorities: { trexImplementationAuthority },
|
|
factories: { identityFactory },
|
|
suite: { token },
|
|
} = await loadFixture(deployFullSuiteFixture);
|
|
|
|
const compliance = await ethers.deployContract('ModularComplianceProxy', [trexImplementationAuthority.address], deployer);
|
|
await token.setCompliance(compliance.address);
|
|
|
|
const trexFactory = await ethers.deployContract('TREXFactory', [trexImplementationAuthority.address, identityFactory.address], deployer);
|
|
|
|
const implementationAuthorityFactory = await ethers.deployContract('IAFactory', [trexFactory.address], deployer);
|
|
await trexImplementationAuthority.setTREXFactory(trexFactory.address);
|
|
await trexImplementationAuthority.setIAFactory(implementationAuthorityFactory.address);
|
|
|
|
const tx = await trexImplementationAuthority.changeImplementationAuthority(token.address, ethers.constants.AddressZero);
|
|
expect(tx).to.emit(trexImplementationAuthority, 'ImplementationAuthorityChanged');
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('when new authority is not the zero address', () => {
|
|
describe('when caller is owner of every contract of the suite of the token', () => {
|
|
describe('when version used by the reference contract is not the same as currently deployed implementations', () => {
|
|
it('should revert', async () => {
|
|
const {
|
|
accounts: { deployer },
|
|
authorities: { trexImplementationAuthority },
|
|
factories: { identityFactory },
|
|
suite: { token },
|
|
implementations,
|
|
} = await loadFixture(deployFullSuiteFixture);
|
|
|
|
const compliance = await ethers.deployContract('ModularComplianceProxy', [trexImplementationAuthority.address], deployer);
|
|
await token.setCompliance(compliance.address);
|
|
|
|
const trexFactory = await ethers.deployContract('TREXFactory', [trexImplementationAuthority.address, identityFactory.address], deployer);
|
|
|
|
const implementationAuthorityFactory = await ethers.deployContract('IAFactory', [trexFactory.address], deployer);
|
|
await trexImplementationAuthority.setTREXFactory(trexFactory.address);
|
|
await trexImplementationAuthority.setIAFactory(implementationAuthorityFactory.address);
|
|
|
|
const otherTrexImplementationAuthority = await ethers.deployContract(
|
|
'TREXImplementationAuthority',
|
|
[true, trexFactory.address, trexImplementationAuthority.address],
|
|
deployer,
|
|
);
|
|
await otherTrexImplementationAuthority.addAndUseTREXVersion(
|
|
{ major: 4, minor: 0, patch: 1 },
|
|
{
|
|
tokenImplementation: implementations.tokenImplementation.address,
|
|
ctrImplementation: implementations.claimTopicsRegistryImplementation.address,
|
|
irImplementation: implementations.identityRegistryImplementation.address,
|
|
irsImplementation: implementations.identityRegistryStorageImplementation.address,
|
|
tirImplementation: implementations.trustedIssuersRegistryImplementation.address,
|
|
mcImplementation: implementations.modularComplianceImplementation.address,
|
|
},
|
|
);
|
|
|
|
await expect(
|
|
trexImplementationAuthority.changeImplementationAuthority(token.address, otherTrexImplementationAuthority.address),
|
|
).to.be.revertedWith('version of new IA has to be the same as current IA');
|
|
});
|
|
});
|
|
|
|
describe('when the new implementation authority is a reference contract but not the current one', () => {
|
|
it('should revert', async () => {
|
|
const {
|
|
accounts: { deployer },
|
|
authorities: { trexImplementationAuthority },
|
|
factories: { identityFactory },
|
|
suite: { token },
|
|
implementations,
|
|
} = await loadFixture(deployFullSuiteFixture);
|
|
|
|
const compliance = await ethers.deployContract('ModularComplianceProxy', [trexImplementationAuthority.address], deployer);
|
|
await token.setCompliance(compliance.address);
|
|
|
|
const trexFactory = await ethers.deployContract('TREXFactory', [trexImplementationAuthority.address, identityFactory.address], deployer);
|
|
|
|
const implementationAuthorityFactory = await ethers.deployContract('IAFactory', [trexFactory.address], deployer);
|
|
await trexImplementationAuthority.setTREXFactory(trexFactory.address);
|
|
await trexImplementationAuthority.setIAFactory(implementationAuthorityFactory.address);
|
|
|
|
const otherTrexImplementationAuthority = await ethers.deployContract(
|
|
'TREXImplementationAuthority',
|
|
[true, trexFactory.address, trexImplementationAuthority.address],
|
|
deployer,
|
|
);
|
|
await otherTrexImplementationAuthority.addAndUseTREXVersion(
|
|
{ major: 4, minor: 0, patch: 0 },
|
|
{
|
|
tokenImplementation: implementations.tokenImplementation.address,
|
|
ctrImplementation: implementations.claimTopicsRegistryImplementation.address,
|
|
irImplementation: implementations.identityRegistryImplementation.address,
|
|
irsImplementation: implementations.identityRegistryStorageImplementation.address,
|
|
tirImplementation: implementations.trustedIssuersRegistryImplementation.address,
|
|
mcImplementation: implementations.modularComplianceImplementation.address,
|
|
},
|
|
);
|
|
|
|
await expect(
|
|
trexImplementationAuthority.changeImplementationAuthority(token.address, otherTrexImplementationAuthority.address),
|
|
).to.be.revertedWith('new IA is NOT reference contract');
|
|
});
|
|
});
|
|
|
|
describe('when the new implementation authority is not a reference contract and is not valid', () => {
|
|
it('should revert', async () => {
|
|
const {
|
|
accounts: { deployer },
|
|
authorities: { trexImplementationAuthority },
|
|
factories: { identityFactory },
|
|
suite: { token },
|
|
} = await loadFixture(deployFullSuiteFixture);
|
|
|
|
const compliance = await ethers.deployContract('ModularComplianceProxy', [trexImplementationAuthority.address], deployer);
|
|
await token.setCompliance(compliance.address);
|
|
|
|
const trexFactory = await ethers.deployContract('TREXFactory', [trexImplementationAuthority.address, identityFactory.address], deployer);
|
|
|
|
const implementationAuthorityFactory = await ethers.deployContract('IAFactory', [trexFactory.address], deployer);
|
|
await trexImplementationAuthority.setTREXFactory(trexFactory.address);
|
|
await trexImplementationAuthority.setIAFactory(implementationAuthorityFactory.address);
|
|
|
|
const otherTrexImplementationAuthority = await ethers.deployContract(
|
|
'TREXImplementationAuthority',
|
|
[false, trexFactory.address, trexImplementationAuthority.address],
|
|
deployer,
|
|
);
|
|
await otherTrexImplementationAuthority.fetchVersion({ major: 4, minor: 0, patch: 0 });
|
|
await otherTrexImplementationAuthority.useTREXVersion({ major: 4, minor: 0, patch: 0 });
|
|
|
|
await expect(
|
|
trexImplementationAuthority.changeImplementationAuthority(token.address, otherTrexImplementationAuthority.address),
|
|
).to.be.revertedWith('invalid IA');
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|