Skip to content

Commit 045ccc1

Browse files
adamdossamaxsam4
authored andcommitted
Audit 3.4 & 3.14 Fix ST upgrades (#694)
* Fixes + test cases * Small change in test * Add fixes for 3.14
1 parent 004194e commit 045ccc1

File tree

6 files changed

+244
-17
lines changed

6 files changed

+244
-17
lines changed

contracts/mocks/MockCountTransferManager.sol

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import "../modules/TransferManager/CTM/CountTransferManager.sol";
88
contract MockCountTransferManager is CountTransferManager {
99

1010
event Upgrader(uint256 _someData);
11+
uint256 public someValue;
1112

1213
/**
1314
* @notice Constructor
@@ -19,6 +20,7 @@ contract MockCountTransferManager is CountTransferManager {
1920

2021
function initialize(uint256 _someData) public {
2122
require(msg.sender == address(this));
23+
someValue = _someData;
2224
emit Upgrader(_someData);
2325
}
2426

contracts/mocks/MockSecurityTokenLogic.sol

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import "../tokens/SecurityToken.sol";
1515
contract MockSecurityTokenLogic is SecurityToken {
1616

1717
event UpgradeEvent(uint256 _upgrade);
18+
uint256 public someValue;
1819

1920
/**
2021
* @notice Initialization function
@@ -23,10 +24,27 @@ contract MockSecurityTokenLogic is SecurityToken {
2324
*/
2425
function upgrade(address _getterDelegate, uint256 _upgrade) external {
2526
getterDelegate = _getterDelegate;
27+
someValue = _upgrade;
2628
//securityTokenVersion = SemanticVersion(3, 1, 0);
2729
emit UpgradeEvent(_upgrade);
2830
}
2931

32+
/**
33+
* @notice Initialization function
34+
* @dev Expected to be called atomically with the proxy being created, by the owner of the token
35+
* @dev Can only be called once
36+
*/
37+
function initialize(address _getterDelegate, uint256 _someValue) public {
38+
//Expected to be called atomically with the proxy being created
39+
require(!initialized, "Already initialized");
40+
getterDelegate = _getterDelegate;
41+
securityTokenVersion = SemanticVersion(3, 0, 0);
42+
updateFromRegistry();
43+
tokenFactory = msg.sender;
44+
initialized = true;
45+
someValue = _someValue;
46+
}
47+
3048
function newFunction(uint256 _upgrade) external {
3149
emit UpgradeEvent(_upgrade);
3250
}

contracts/modules/UpgradableModuleFactory.sol

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import "../proxy/OwnedUpgradeabilityProxy.sol";
1010
*/
1111
contract UpgradableModuleFactory is ModuleFactory {
1212

13-
event LogicContractSet(string _version, address _logicContract, bytes _upgradeData);
13+
event LogicContractSet(string _version, uint256 _upgrade, address _logicContract, bytes _upgradeData);
1414

1515
event ModuleUpgraded(
1616
address indexed _module,
@@ -68,12 +68,35 @@ contract UpgradableModuleFactory is ModuleFactory {
6868
require(_logicContract != logicContracts[latestUpgrade].logicContract, "Same version");
6969
require(_logicContract != address(0), "Invalid address");
7070
latestUpgrade++;
71-
logicContracts[latestUpgrade].version = _version;
72-
logicContracts[latestUpgrade].logicContract = _logicContract;
73-
logicContracts[latestUpgrade].upgradeData = _upgradeData;
71+
_modifyLogicContract(latestUpgrade, _version, _logicContract, _upgradeData);
72+
}
73+
74+
/**
75+
* @notice Used to update an existing token logic contract
76+
* @param _upgrade logic contract to upgrade
77+
* @param _version Version of upgraded module
78+
* @param _logicContract Address of deployed module logic contract referenced from proxy
79+
* @param _upgradeData Data to be passed in call to upgradeToAndCall when a token upgrades its module
80+
*/
81+
function updateLogicContract(uint256 _upgrade, string calldata _version, address _logicContract, bytes calldata _upgradeData) external onlyOwner {
82+
require(_upgrade <= latestUpgrade, "Invalid upgrade");
83+
// version & contract must differ from previous version, otherwise upgrade proxy will fail
84+
if (_upgrade > 0) {
85+
require(keccak256(abi.encodePacked(_version)) != keccak256(abi.encodePacked(logicContracts[_upgrade - 1].version)), "Same version");
86+
require(_logicContract != logicContracts[_upgrade - 1].logicContract, "Same version");
87+
}
88+
require(_logicContract != address(0), "Invalid address");
89+
require(_upgradeData.length > 4, "Invalid Upgrade");
90+
_modifyLogicContract(_upgrade, _version, _logicContract, _upgradeData);
91+
}
92+
93+
function _modifyLogicContract(uint256 _upgrade, string memory _version, address _logicContract, bytes memory _upgradeData) internal {
94+
logicContracts[_upgrade].version = _version;
95+
logicContracts[_upgrade].logicContract = _logicContract;
96+
logicContracts[_upgrade].upgradeData = _upgradeData;
7497
IModuleRegistry moduleRegistry = IModuleRegistry(IPolymathRegistry(polymathRegistry).getAddress("ModuleRegistry"));
7598
moduleRegistry.unverifyModule(address(this));
76-
emit LogicContractSet(_version, _logicContract, _upgradeData);
99+
emit LogicContractSet(_version, _upgrade, _logicContract, _upgradeData);
77100
}
78101

79102
/**

contracts/tokens/STFactory.sol

Lines changed: 39 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ contract STFactory is ISTFactory, Ownable {
3535

3636
uint256 public latestUpgrade;
3737

38-
event LogicContractSet(string _version, address _logicContract, bytes _upgradeData);
38+
event LogicContractSet(string _version, uint256 _upgrade, address _logicContract, bytes _initializationData, bytes _upgradeData);
3939
event TokenUpgraded(
4040
address indexed _securityToken,
4141
uint256 indexed _version
@@ -50,13 +50,14 @@ contract STFactory is ISTFactory, Ownable {
5050
string memory _version,
5151
address _logicContract,
5252
bytes memory _initializationData
53-
)
54-
public
53+
)
54+
public
5555
{
5656
require(_logicContract != address(0), "Invalid Address");
5757
require(_transferManagerFactory != address(0), "Invalid Address");
5858
require(_dataStoreFactory != address(0), "Invalid Address");
5959
require(_polymathRegistry != address(0), "Invalid Address");
60+
require(_initializationData.length > 4, "Invalid Initialization");
6061
transferManagerFactory = _transferManagerFactory;
6162
dataStoreFactory = DataStoreFactory(_dataStoreFactory);
6263
polymathRegistry = IPolymathRegistry(_polymathRegistry);
@@ -121,6 +122,7 @@ contract STFactory is ISTFactory, Ownable {
121122
address(polymathRegistry)
122123
);
123124
// Sets logic contract
125+
emit Log(latestUpgrade);
124126
proxy.upgradeTo(logicContracts[latestUpgrade].version, logicContracts[latestUpgrade].logicContract);
125127
// Initialises security token contract - needed for functions that can only be called by the
126128
// owner of the contract, or are specific to this particular logic contract (e.g. setting version)
@@ -130,23 +132,52 @@ contract STFactory is ISTFactory, Ownable {
130132
return address(proxy);
131133
}
132134

135+
event Log(uint256 _upgrade);
136+
133137
/**
134138
* @notice Used to set a new token logic contract
135139
* @param _version Version of upgraded module
136140
* @param _logicContract Address of deployed module logic contract referenced from proxy
137141
* @param _upgradeData Data to be passed in call to upgradeToAndCall when a token upgrades its module
138142
*/
139-
function setLogicContract(string calldata _version, address _logicContract, bytes calldata _upgradeData) external onlyOwner {
143+
function setLogicContract(string calldata _version, address _logicContract, bytes calldata _initializationData, bytes calldata _upgradeData) external onlyOwner {
140144
require(keccak256(abi.encodePacked(_version)) != keccak256(abi.encodePacked(logicContracts[latestUpgrade].version)), "Same version");
141145
require(_logicContract != logicContracts[latestUpgrade].logicContract, "Same version");
142146
require(_logicContract != address(0), "Invalid address");
147+
require(_initializationData.length > 4, "Invalid Initialization");
148+
require(_upgradeData.length > 4, "Invalid Upgrade");
143149
latestUpgrade++;
144-
logicContracts[latestUpgrade].version = _version;
145-
logicContracts[latestUpgrade].logicContract = _logicContract;
146-
logicContracts[latestUpgrade].upgradeData = _upgradeData;
147-
emit LogicContractSet(_version, _logicContract, _upgradeData);
150+
_modifyLogicContract(latestUpgrade, _version, _logicContract, _initializationData, _upgradeData);
151+
}
152+
153+
/**
154+
* @notice Used to update an existing token logic contract
155+
* @param _upgrade logic contract to upgrade
156+
* @param _version Version of upgraded module
157+
* @param _logicContract Address of deployed module logic contract referenced from proxy
158+
* @param _upgradeData Data to be passed in call to upgradeToAndCall when a token upgrades its module
159+
*/
160+
function updateLogicContract(uint256 _upgrade, string calldata _version, address _logicContract, bytes calldata _initializationData, bytes calldata _upgradeData) external onlyOwner {
161+
require(_upgrade <= latestUpgrade, "Invalid upgrade");
162+
require(_upgrade > 0, "Invalid upgrade");
163+
// version & contract must differ from previous version, otherwise upgrade proxy will fail
164+
if (_upgrade > 1) {
165+
require(keccak256(abi.encodePacked(_version)) != keccak256(abi.encodePacked(logicContracts[_upgrade - 1].version)), "Same version");
166+
require(_logicContract != logicContracts[_upgrade - 1].logicContract, "Same version");
167+
}
168+
require(_logicContract != address(0), "Invalid address");
169+
require(_initializationData.length > 4, "Invalid Initialization");
170+
require(_upgradeData.length > 4, "Invalid Upgrade");
171+
_modifyLogicContract(_upgrade, _version, _logicContract, _initializationData, _upgradeData);
148172
}
149173

174+
function _modifyLogicContract(uint256 _upgrade, string memory _version, address _logicContract, bytes memory _initializationData, bytes memory _upgradeData) internal {
175+
logicContracts[_upgrade].version = _version;
176+
logicContracts[_upgrade].logicContract = _logicContract;
177+
logicContracts[_upgrade].upgradeData = _upgradeData;
178+
logicContracts[_upgrade].initializationData = _initializationData;
179+
emit LogicContractSet(_version, _upgrade, _logicContract, _initializationData, _upgradeData);
180+
}
150181
/**
151182
* @notice Used to upgrade a token
152183
* @param _maxModuleType maximum module type enumeration

test/d_count_transfer_manager.js

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,10 @@ contract("CountTransferManager", async (accounts) => {
3737
let I_GeneralPermissionManager;
3838
let I_CountTransferManager;
3939
let I_CountTransferManager2;
40+
let I_CountTransferManager3;
4041
let I_GeneralTransferManager;
4142
let I_GeneralTransferManager2;
43+
let I_GeneralTransferManager3;
4244
let I_ExchangeTransferManager;
4345
let I_ModuleRegistry;
4446
let I_ModuleRegistryProxy;
@@ -49,17 +51,21 @@ contract("CountTransferManager", async (accounts) => {
4951
let I_STFactory;
5052
let I_SecurityToken;
5153
let I_SecurityToken2;
54+
let I_SecurityToken3;
5255
let I_PolyToken;
5356
let I_PolymathRegistry;
5457
let I_STRGetter;
5558
let I_STGetter;
59+
let I_MockCountTransferManagerLogic;
5660
let stGetter;
5761
let stGetter2;
62+
let stGetter3;
5863

5964
// SecurityToken Details
6065
const name = "Team";
6166
const symbol = "sap";
6267
const symbol2 = "sapp";
68+
const symbol3 = "sapp3"
6369
const tokenDetails = "This is equity type of issuance";
6470
const decimals = 18;
6571
const contact = "[email protected]";
@@ -373,6 +379,21 @@ contract("CountTransferManager", async (accounts) => {
373379
I_GeneralTransferManager2 = await GeneralTransferManager.at(moduleData);
374380
});
375381

382+
it("deploy another new token & auto attach modules", async () => {
383+
//register ticker and deploy token
384+
await I_PolyToken.approve(I_STRProxied.address, initRegFee, { from: token_owner });
385+
let tx = await I_STRProxied.registerTicker(token_owner, symbol3, contact, { from: token_owner });
386+
387+
await I_PolyToken.approve(I_STRProxied.address, initRegFee, { from: token_owner });
388+
389+
let tx2 = await I_STRProxied.generateNewSecurityToken(name, symbol3, tokenDetails, false, token_owner, 0, { from: token_owner });
390+
391+
I_SecurityToken3 = await SecurityToken.at(tx2.logs[1].args._securityTokenAddress);
392+
stGetter3 = await STGetter.at(I_SecurityToken3.address);
393+
let moduleData = (await stGetter3.getModulesByType(2))[0];
394+
I_GeneralTransferManager3 = await GeneralTransferManager.at(moduleData);
395+
});
396+
376397
it("add 3 holders to the token", async () => {
377398
await I_GeneralTransferManager2.modifyKYCData(
378399
account_investor1,
@@ -455,8 +476,21 @@ contract("CountTransferManager", async (accounts) => {
455476
console.log("current max holder number is " + (await I_CountTransferManager2.maxHolderCount({ from: token_owner })));
456477
});
457478

479+
it("Should successfully attach the CountTransferManager with the third security token and set max holder to 2", async () => {
480+
const tx = await I_SecurityToken3.addModule(I_CountTransferManagerFactory.address, bytesSTO, new BN(0), new BN(0), false, { from: token_owner });
481+
assert.equal(tx.logs[2].args._types[0].toNumber(), transferManagerKey, "CountTransferManager doesn't get deployed");
482+
assert.equal(
483+
web3.utils.toAscii(tx.logs[2].args._name).replace(/\u0000/g, ""),
484+
"CountTransferManager",
485+
"CountTransferManager module was not added"
486+
);
487+
I_CountTransferManager3 = await CountTransferManager.at(tx.logs[2].args._module);
488+
await I_CountTransferManager3.changeHolderCount(2, { from: token_owner });
489+
console.log("current max holder number is " + (await I_CountTransferManager3.maxHolderCount({ from: token_owner })));
490+
});
491+
458492
it("Should upgrade the CTM", async () => {
459-
let I_MockCountTransferManagerLogic = await MockCountTransferManager.new("0x0000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000", { from: account_polymath });
493+
I_MockCountTransferManagerLogic = await MockCountTransferManager.new("0x0000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000", { from: account_polymath });
460494
let bytesCM = encodeProxyCall(["uint256"], [11]);
461495
await catchRevert(
462496
// Fails as no upgrade available
@@ -482,9 +516,39 @@ contract("CountTransferManager", async (accounts) => {
482516
let tx = await I_MRProxied.verifyModule(I_CountTransferManagerFactory.address, { from: account_polymath });
483517
await I_SecurityToken2.upgradeModule(I_CountTransferManager2.address, { from: token_owner });
484518
let I_MockCountTransferManager = await MockCountTransferManager.at(I_CountTransferManager2.address);
519+
let newValue = await I_MockCountTransferManager.someValue.call();
520+
assert(newValue.toNumber(), 11);
485521
await I_MockCountTransferManager.newFunction();
486522
});
487523

524+
it("Should modify the upgrade data and upgrade", async () => {
525+
let bytesCM = encodeProxyCall(["uint256"], [12]);
526+
await catchRevert(
527+
// Fails due to the same version being used
528+
I_CountTransferManagerFactory.updateLogicContract(1, "3.0.0", I_MockCountTransferManagerLogic.address, bytesCM, { from: account_polymath })
529+
);
530+
await catchRevert(
531+
// Fails due to the wrong contract being used
532+
I_CountTransferManagerFactory.updateLogicContract(1, "4.0.0", "0x0000000000000000000000000000000000000000", bytesCM, { from: account_polymath })
533+
);
534+
await catchRevert(
535+
// Fails due to the wrong owner being used
536+
I_CountTransferManagerFactory.updateLogicContract(1, "4.0.0", "0x0000000000000000000000000000000000000000", bytesCM, { from: token_owner })
537+
);
538+
await I_CountTransferManagerFactory.updateLogicContract(1, "4.0.0", I_MockCountTransferManagerLogic.address, bytesCM, { from: account_polymath });
539+
await catchRevert(
540+
// Fails as upgraded module has been unverified
541+
I_SecurityToken3.upgradeModule(I_CountTransferManager3.address, { from: token_owner })
542+
);
543+
let tx = await I_MRProxied.verifyModule(I_CountTransferManagerFactory.address, { from: account_polymath });
544+
await I_SecurityToken3.upgradeModule(I_CountTransferManager3.address, { from: token_owner });
545+
let I_MockCountTransferManager = await MockCountTransferManager.at(I_CountTransferManager3.address);
546+
let newValue = await I_MockCountTransferManager.someValue.call();
547+
assert(newValue.toNumber(), 12);
548+
await I_MockCountTransferManager.newFunction();
549+
550+
});
551+
488552
it("Should upgrade the CTM again", async () => {
489553
let I_MockCountTransferManagerLogic = await MockCountTransferManager.new("0x0000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000", { from: account_polymath });
490554
let bytesCM = encodeProxyCall(["uint256"], [11]);

0 commit comments

Comments
 (0)