Skip to content

Commit

Permalink
Merge pull request #263 from OriginTrail/operatorFee-fixes
Browse files Browse the repository at this point in the history
operator fee fixes
  • Loading branch information
u-hubar authored May 30, 2024
2 parents b5236cf + a009e24 commit eff947e
Show file tree
Hide file tree
Showing 6 changed files with 188 additions and 102 deletions.
13 changes: 13 additions & 0 deletions abi/Profile.json
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,19 @@
"stateMutability": "pure",
"type": "function"
},
{
"inputs": [],
"name": "nodeOperatorFeesStorage",
"outputs": [
{
"internalType": "contract NodeOperatorFeesStorage",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "parametersStorage",
Expand Down
8 changes: 6 additions & 2 deletions contracts/v1/Profile.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {ParametersStorage} from "./storage/ParametersStorage.sol";
import {ProfileStorage} from "./storage/ProfileStorage.sol";
import {StakingStorage} from "./storage/StakingStorage.sol";
import {Staking} from "./Staking.sol";
import {NodeOperatorFeesStorage} from "../v2/storage/NodeOperatorFeesStorage.sol";
import {WhitelistStorage} from "./storage/WhitelistStorage.sol";
import {ContractStatus} from "./abstract/ContractStatus.sol";
import {Initializable} from "./interface/Initializable.sol";
Expand All @@ -33,7 +34,7 @@ contract Profile is Named, Versioned, ContractStatus, Initializable {
event AskUpdated(uint72 indexed identityId, bytes nodeId, uint96 ask);

string private constant _NAME = "Profile";
string private constant _VERSION = "1.1.1";
string private constant _VERSION = "1.1.2";

HashingProxy public hashingProxy;
Identity public identityContract;
Expand All @@ -43,6 +44,7 @@ contract Profile is Named, Versioned, ContractStatus, Initializable {
ParametersStorage public parametersStorage;
ProfileStorage public profileStorage;
WhitelistStorage public whitelistStorage;
NodeOperatorFeesStorage public nodeOperatorFeesStorage;

// solhint-disable-next-line no-empty-blocks
constructor(address hubAddress) ContractStatus(hubAddress) {}
Expand Down Expand Up @@ -76,6 +78,7 @@ contract Profile is Named, Versioned, ContractStatus, Initializable {
parametersStorage = ParametersStorage(hub.getContractAddress("ParametersStorage"));
profileStorage = ProfileStorage(hub.getContractAddress("ProfileStorage"));
whitelistStorage = WhitelistStorage(hub.getContractAddress("WhitelistStorage"));
nodeOperatorFeesStorage = NodeOperatorFeesStorage(hub.getContractAddress("NodeOperatorFeesStorage"));
}

function name() external pure virtual override returns (string memory) {
Expand All @@ -96,6 +99,7 @@ contract Profile is Named, Versioned, ContractStatus, Initializable {
) external onlyWhitelisted {
IdentityStorage ids = identityStorage;
ProfileStorage ps = profileStorage;
NodeOperatorFeesStorage nofs = nodeOperatorFeesStorage;
Identity id = identityContract;

if (ids.getIdentityId(msg.sender) != 0) {
Expand Down Expand Up @@ -136,7 +140,7 @@ contract Profile is Named, Versioned, ContractStatus, Initializable {
ps.createProfile(identityId, nodeId, address(sharesContract));
_setAvailableNodeAddresses(identityId);

stakingStorage.setOperatorFee(identityId, initialOperatorFee);
nofs.addOperatorFee(identityId, initialOperatorFee, uint248(block.timestamp));

emit ProfileCreated(identityId, nodeId, adminWallet, address(sharesContract), initialOperatorFee);
}
Expand Down
10 changes: 4 additions & 6 deletions contracts/v2/storage/NodeOperatorFeesStorage.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {NodeOperatorStructs} from "../structs/NodeOperatorStructs.sol";

contract NodeOperatorFeesStorage is Named, Versioned, HubDependent {
string private constant _NAME = "NodeOperatorFeesStorage";
string private constant _VERSION = "2.0.0";
string private constant _VERSION = "2.0.2";

bool private _delayFreePeriodSet;
uint256 public delayFreePeriodEnd;
Expand All @@ -19,7 +19,6 @@ contract NodeOperatorFeesStorage is Named, Versioned, HubDependent {
// identityId => OperatorFee[]
mapping(uint72 => NodeOperatorStructs.OperatorFee[]) public operatorFees;

// solhint-disable-next-line no-empty-blocks
constructor(address hubAddress, uint256 migrationPeriodEnd_) HubDependent(hubAddress) {
migrationPeriodEnd = migrationPeriodEnd_;
}
Expand All @@ -43,12 +42,11 @@ contract NodeOperatorFeesStorage is Named, Versioned, HubDependent {
return _VERSION;
}

function migrateOldOperatorFees(
NodeOperatorStructs.OperatorFees[] memory legacyFees
) external onlyHubOwner timeLimited {
function migrateOldOperatorFees(NodeOperatorStructs.OperatorFees[] memory legacyFees) external timeLimited {
for (uint i; i < legacyFees.length; ) {
operatorFees[legacyFees[i].identityId] = legacyFees[i].fees;
require(operatorFees[legacyFees[i].identityId].length == 0);

operatorFees[legacyFees[i].identityId] = legacyFees[i].fees;
unchecked {
i++;
}
Expand Down
207 changes: 139 additions & 68 deletions deploy/028_deploy_node_operator_fees_storage.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { BigNumberish } from 'ethers';
import { DeployFunction } from 'hardhat-deploy/types';
import { HardhatRuntimeEnvironment } from 'hardhat/types';

import { ShardingTableStructsV1 } from '../typechain/contracts/v2/ShardingTable.sol/ShardingTableV2';

const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
const isDeployed = hre.helpers.isDeployed('NodeOperatorFeesStorage');
const isMigration = hre.helpers.contractDeployments.contracts['NodeOperatorFeesStorage']?.migration || false;

if (isDeployed) {
if (isDeployed && !isMigration) {
return;
}

Expand All @@ -15,22 +15,12 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {

const { deployer } = await hre.getNamedAccounts();

let shardingTableABI;
let shardingTableAddress;
if (Object.keys(hre.helpers.contractDeployments.contracts).includes('OldShardingTable')) {
shardingTableABI = hre.helpers.getAbi('ShardingTable');
shardingTableAddress = hre.helpers.contractDeployments.contracts['OldShardingTable'].evmAddress;
delete hre.helpers.contractDeployments.contracts['OldShardingTable'];
console.log(`Found V1 ShardingTable address: ${shardingTableAddress}`);
} else {
shardingTableABI = hre.helpers.getAbi('ShardingTableV2');
shardingTableAddress = hre.helpers.contractDeployments.contracts['ShardingTable'].evmAddress;
console.log(`Found V2 ShardingTable address: ${shardingTableAddress}`);
}
const ShardingTable = await hre.ethers.getContractAt(shardingTableABI, shardingTableAddress, deployer);

const stakingStorageAddress = hre.helpers.contractDeployments.contracts['StakingStorage'].evmAddress;
const StakingStorage = await hre.ethers.getContractAt('StakingStorage', stakingStorageAddress, deployer);
const oldNodeOperatorFeesStorageAddress =
hre.helpers.contractDeployments.contracts['NodeOperatorFeesStorage'].evmAddress;
const OldNodeOperatorFeesStorage = await hre.ethers.getContractAt(
'NodeOperatorFeesStorage',
oldNodeOperatorFeesStorageAddress,
);

const nofcsAddress = hre.helpers.contractDeployments.contracts['NodeOperatorFeeChangesStorage']?.evmAddress;
let nofcs = null;
Expand All @@ -39,43 +29,133 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
nofcs = await hre.ethers.getContractAt(abi, nofcsAddress, deployer);
}

console.log(`Getting list of nodes in Sharding Table...`);
const nodes: ShardingTableStructsV1.NodeInfoStructOutput[] = await ShardingTable['getShardingTable()']();
const identityIds = nodes.map((node) => node.identityId);
const stakingStorageAddress = hre.helpers.contractDeployments.contracts['StakingStorage'].evmAddress;
const StakingStorage = await hre.ethers.getContractAt('StakingStorage', stakingStorageAddress, deployer);

console.log(`Starting migration of the old operator fees...`);
for (const identityId of identityIds) {
const storageLayout = {
astId: 1238,
contract: 'IdentityStorageFlattened.sol:IdentityStorage',
label: '_identityId',
offset: 20,
slot: 0,
type: 't_uint72',
};
const storageVariableType = {
t_uint72: {
encoding: 'inplace',
label: 'uint72',
numberOfBytes: 9,
},
};

console.log('Getting current next identityId from IdentityStorage...');
const storageSlot = await hre.ethers.provider.getStorageAt(
hre.helpers.contractDeployments.contracts['IdentityStorage'].evmAddress,
0,
);
const variableSlot = storageSlot.slice(
storageSlot.length -
2 *
(storageLayout.offset +
storageVariableType[storageLayout.type as keyof typeof storageVariableType].numberOfBytes),
storageSlot.length - storageLayout.offset * 2,
);
console.log(`Storage slot ${storageLayout.slot}: ${storageSlot}`);
console.log(`Variable slot: ${variableSlot}`);
const nextIdentityId = parseInt(variableSlot, 16);
console.log(`Current next identityId: ${nextIdentityId}`);

console.log(`Starting migration of the old operator fees... Latest identityId: ${nextIdentityId - 1}`);
for (let identityId = 1; identityId < nextIdentityId; identityId++) {
console.log(`--------------------------------------------------------`);
console.log(`IdentityId: ${identityId}`);

const operatorFees = [];
let operatorFees: { feePercentage: BigNumberish; effectiveDate: number }[] = [];

const activeOperatorFeePercentage = await StakingStorage.operatorFees(identityId);
const oldContractOperatorFees = await OldNodeOperatorFeesStorage.getOperatorFees(identityId);

console.log(`Active operatorFee in the StakingStorage: ${activeOperatorFeePercentage.toString()}%`);
console.log(`Old operatorFees in the old NodeOperatorFeesStorage: ${JSON.stringify(oldContractOperatorFees)}`);

if (!activeOperatorFeePercentage.eq(0)) {
operatorFees.push({
feePercentage: activeOperatorFeePercentage,
effectiveDate: timestampNow,
if (oldContractOperatorFees.length != 0) {
const fees = oldContractOperatorFees.map((x: BigNumberish[]) => {
return { feePercentage: x[0], effectiveDate: Number(x[1].toString()) };
});

if (hre.network.name.startsWith('gnosis')) {
operatorFees = operatorFees.concat(fees);
} else {
oldOperatorFees.push({
identityId,
fees,
});
continue;
}
}

let stakingStorageSource = false;
if (operatorFees.length == 0) {
const activeOperatorFeePercentage = await StakingStorage.operatorFees(identityId);

console.log(`Active operatorFee in the StakingStorage: ${activeOperatorFeePercentage.toString()}%`);

if (!activeOperatorFeePercentage.eq(0)) {
operatorFees.push({
feePercentage: activeOperatorFeePercentage,
effectiveDate: timestampNow,
});
stakingStorageSource = true;
}
}

if (nofcs !== null) {
const pendingOperatorFee = await nofcs.operatorFeeChangeRequests(identityId);

if (Number(pendingOperatorFee.timestamp.toString() == 0)) {
if (operatorFees.length > 0) {
oldOperatorFees.push({
identityId,
fees: operatorFees,
});
} else {
oldOperatorFees.push({
identityId,
fees: [{ feePercentage: 0, effectiveDate: timestampNow }],
});
}
continue;
}

console.log(`Pending operatorFee in the NodeOperatorFeeChangesStorage: ${pendingOperatorFee.newFee.toString()}%`);

if (!pendingOperatorFee.timestamp.eq(0)) {
if (pendingOperatorFee.timestamp < operatorFees[0].effectiveDate) {
operatorFees[0].effectiveDate = pendingOperatorFee.timestamp - 1;
}
const exists = operatorFees.some(
(obj) =>
Number(obj.feePercentage.toString()) === Number(pendingOperatorFee.newFee.toString()) &&
Number(obj.effectiveDate.toString()) === Number(pendingOperatorFee.timestamp.toString()),
);

operatorFees.push({
feePercentage: pendingOperatorFee.newFee,
effectiveDate: pendingOperatorFee.timestamp,
if (exists) {
console.log(`Pending operatorFee is already a part of the fees array from old NodeOperatorFeesStorage`);
oldOperatorFees.push({
identityId,
fees: operatorFees,
});
continue;
}

if (
(stakingStorageSource &&
Number(pendingOperatorFee.timestamp.toString()) < Number(operatorFees[0].effectiveDate.toString())) ||
(operatorFees.length == 1 && Number(operatorFees[0].effectiveDate.toString()) == 1716291685)
) {
operatorFees[0].effectiveDate = Number(pendingOperatorFee.timestamp.toString()) - 1;
}

operatorFees.push({
feePercentage: pendingOperatorFee.newFee,
effectiveDate: Number(pendingOperatorFee.timestamp.toString()),
});

operatorFees.sort((a, b) => a.effectiveDate - b.effectiveDate);
}

console.log(`--------------------------------------------------------`);
Expand All @@ -85,6 +165,11 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
identityId,
fees: operatorFees,
});
} else {
oldOperatorFees.push({
identityId,
fees: [{ feePercentage: 0, effectiveDate: timestampNow }],
});
}
}

Expand All @@ -98,38 +183,24 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
});

const chunkSize = 10;
const encodedDataArray: string[] = oldOperatorFees.reduce<string[]>((acc, _, currentIndex, array) => {
if (currentIndex % chunkSize === 0) {
// Encode and push the function data for a slice of the array
acc.push(
NodeOperatorFeesStorage.interface.encodeFunctionData('migrateOldOperatorFees', [
array.slice(currentIndex, currentIndex + chunkSize),
]),
);
}
return acc;
}, []);

if (hre.network.config.environment === 'development') {
const { deployer } = await hre.getNamedAccounts();

const hubControllerAddress = hre.helpers.contractDeployments.contracts['HubController'].evmAddress;
const HubController = await hre.ethers.getContractAt('HubController', hubControllerAddress, deployer);

for (let i = 0; i < encodedDataArray.length; i++) {
const migrateOldOperatorFeesTx = await HubController.forwardCall(
NodeOperatorFeesStorage.address,
encodedDataArray[i],
);
await migrateOldOperatorFeesTx.wait();
}
} else {
for (let i = 0; i < encodedDataArray.length; i++) {
hre.helpers.setParametersEncodedData.push(['NodeOperatorFeesStorage', [encodedDataArray[i]]]);
}
const totalChunks = Math.ceil(oldOperatorFees.length / chunkSize);

console.log(`Starting migration of operator fees for ${oldOperatorFees.length} nodes...`);
for (let i = 0; i < oldOperatorFees.length; i += chunkSize) {
const chunk = oldOperatorFees.slice(i, i + chunkSize);
const chunkNumber = Math.floor(i / chunkSize) + 1;
const percentageDone = ((chunkNumber / totalChunks) * 100).toFixed(2);
console.log(
`Processing chunk ${chunkNumber} out of ${totalChunks} (starting at index ${i}):`,
JSON.stringify(chunk),
);
console.log(`Percentage done: ${percentageDone}%`);

const tx = await NodeOperatorFeesStorage.migrateOldOperatorFees(chunk);
await tx.wait();
}
};

export default func;
func.tags = ['NodeOperatorFeesStorage', 'v2'];
func.dependencies = ['HubV2', 'StakingStorage', 'ShardingTableV2'];
func.dependencies = ['HubV2', 'ContentAssetStorageV2', 'StakingStorage', 'ShardingTableV2'];
Loading

0 comments on commit eff947e

Please sign in to comment.