Skip to content

Commit

Permalink
Fix multi-contrib nodes setting contract as operator of node
Browse files Browse the repository at this point in the history
  • Loading branch information
Doy-lee committed Oct 17, 2024
1 parent 4a02e56 commit 860a5e0
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 64 deletions.
34 changes: 30 additions & 4 deletions contracts/ServiceNodeRewards.sol
Original file line number Diff line number Diff line change
Expand Up @@ -377,16 +377,42 @@ contract ServiceNodeRewards is Initializable, Ownable2StepUpgradeable, PausableU
if (serviceNodeID != 0)
revert BLSPubkeyAlreadyExists(serviceNodeID);

_validateProofOfPossession(blsPubkey, blsSignature, msg.sender, serviceNodeParams.serviceNodePubkey);
// NOTE: 1st contributor _may_ differ from `msg.sender`. In a solo node
// configuration, the operator calls `addBLSPublicKey` directly, then,
// `msg.sender` is the same as the 1st contributor:
//
// msg.sender => operator
// 1st contributor => operator
//
// In a multi-contribution configuration, a multi-contribution contract
// is instantiated and that contract calls `addBLSPublicKey` when the
// stake is fulfilled. This leads to the other scenario:
//
// msg.sender => address of multi-contribution contract
// 1st contributor => actual operator
//
// It is not possible to instantiate a multi-contribution contract with
// 0 contributors so it's impossible to assign the operator as the
// multi-contribution contract itself in the branch where no
// contributors are specified.
//
// Because of this, there's a distinction between whether we use the
// operator _or_ msg.sender in the following code. We transfer tokens
// from `msg.sender` but we validate the proof-of-possession with the
// operator.
address operator = contributors[0].staker.addr;
_validateProofOfPossession(blsPubkey, blsSignature, operator, serviceNodeParams.serviceNodePubkey);

(uint64 allocID, ServiceNode storage sn) = serviceNodeAdd(blsPubkey, serviceNodeParams.serviceNodePubkey);
sn.deposit = stakingRequirement;
sn.operator = contributors[0].staker.addr;
for (uint256 i = 0; i < contributors.length; i++)
sn.operator = operator;
for (uint256 i = 0; i < contributors.length; ) {
sn.contributors.push(contributors[i]);
unchecked { i++; }
}

updateBLSNonSignerThreshold();
emit NewServiceNodeV2(0, allocID, msg.sender, blsPubkey, serviceNodeParams, contributors);
emit NewServiceNodeV2(0, allocID, operator, blsPubkey, serviceNodeParams, contributors);
SafeERC20.safeTransferFrom(designatedToken, msg.sender, address(this), stakingRequirement);
}

Expand Down
96 changes: 36 additions & 60 deletions scripts/deploy-local-test.js
Original file line number Diff line number Diff line change
@@ -1,67 +1,43 @@
// We require the Hardhat Runtime Environment explicitly here. This is optional
// but useful for running the script in a standalone fashion through `node <script>`.
//
// You can also run a script with `npx hardhat run <script>`. If you do that, Hardhat
// will compile your contracts, add the Hardhat Runtime Environment's members to the
// global scope, and execute the script.
const hre = require("hardhat");
const hre = require("hardhat");
const chalk = require('chalk')

let principal = 40000000;
let bigAtomicPrincipal = ethers.parseUnits(principal.toString(), 9);

async function main() {
// Deploy a mock ERC20 token
try {
// Deploy a mock ERC20 token
MockERC20 = await ethers.getContractFactory("MockERC20");
mockERC20 = await MockERC20.deploy("SENT Token", "SENT", 9);
} catch (error) {
console.error("Error deploying MockERC20:", error);
}

// Get signers
[owner] = await ethers.getSigners();

RewardRatePool = await ethers.getContractFactory("RewardRatePool");
rewardRatePool = await upgrades.deployProxy(RewardRatePool, [await owner.getAddress(), await mockERC20.getAddress()]);

await mockERC20.transfer(rewardRatePool, bigAtomicPrincipal);

// Deploy the ServiceNodeRewards contract
ServiceNodeRewardsMaster = await ethers.getContractFactory("ServiceNodeRewards");
serviceNodeRewards = await upgrades.deployProxy(ServiceNodeRewardsMaster,[
await mockERC20.getAddress(), // token address
await rewardRatePool.getAddress(), // foundation pool address
120_000_000_000, // staking requirement
10, // max contributors
1, // liquidator reward ratio
0, // pool share of liquidation ratio
1 // recipient ratio
// NOTE: Deploy tokens
token_deployer = await ethers.getContractFactory("MockERC20");
token = await token_deployer.deploy("SENT Token", "SENT", 9);
[owner] = await ethers.getSigners();

// NOTE: Deploy the reward pool contract
const reward_rate_pool_deployer = await ethers.getContractFactory("RewardRatePool");
const reward_rate_pool = await upgrades.deployProxy(reward_rate_pool_deployer, [await owner.getAddress(), await token.getAddress()]);
await reward_rate_pool.waitForDeployment();

// NOTE: Fund the reward pool
await token.transfer(reward_rate_pool, 40_000_000n * BigInt(1e9));

// NOTE: Deploy the rewards contract
const sn_rewards_deployer = await ethers.getContractFactory("ServiceNodeRewards");
const sn_rewards = await upgrades.deployProxy(sn_rewards_deployer, [
await token.getAddress(), // token address
await reward_rate_pool.getAddress(), // foundation pool address
120n * BigInt(1e9), // staking requirement
10, // max contributors
1, // liquidator reward ratio
0, // pool share of liquidation ratio
1 // recipient ratio
]);


await serviceNodeRewards.waitForDeployment();
const leng = serviceNodeRewards.totalNodes();

console.log(
' ',
chalk.cyan(`Service Node Rewards Contract`),
'deployed to:',
chalk.greenBright(await serviceNodeRewards.getAddress()),
)
console.log(
' ',
chalk.cyan(`Reward Rate Pool Contract`),
'deployed to:',
chalk.greenBright(await rewardRatePool.getAddress()),
)
console.log(
' ',
chalk.cyan(`SENT Contract Address`),
'deployed to:',
chalk.greenBright(await mockERC20.getAddress()),
)
await sn_rewards.waitForDeployment();

// NOTE: Deploy the multi contribution factory
const sn_contrib_factory_deployer = await ethers.getContractFactory("ServiceNodeContributionFactory");
const sn_contrib_factory = await sn_contrib_factory_deployer.deploy(await sn_rewards.getAddress());
await sn_contrib_factory.waitForDeployment();

// NOTE: Output contract addresses
console.log(' ', chalk.cyan(`Service Node Rewards Contract`), ' deployed to:', chalk.greenBright(await sn_rewards.getAddress()))
console.log(' ', chalk.cyan(`Service Node Contribution Factory`), 'deployed to:', chalk.greenBright(await sn_contrib_factory.getAddress()))
console.log(' ', chalk.cyan(`Reward Rate Pool Contract`), ' deployed to:', chalk.greenBright(await reward_rate_pool.getAddress()))
console.log(' ', chalk.cyan(`SENT Contract Address`), ' deployed to:', chalk.greenBright(await token.getAddress()))
}

// We recommend this pattern to be able to use async/await everywhere
Expand Down

0 comments on commit 860a5e0

Please sign in to comment.