Skip to content

Commit

Permalink
add permission to handle toekn rate report by token rate notifier
Browse files Browse the repository at this point in the history
  • Loading branch information
kovalgek committed Jun 14, 2024
1 parent baf9859 commit 12333ef
Show file tree
Hide file tree
Showing 11 changed files with 82 additions and 455 deletions.
15 changes: 14 additions & 1 deletion contracts/lido/TokenRateNotifier.sol
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ interface IPostTokenRebaseReceiver {
contract TokenRateNotifier is Ownable, IPostTokenRebaseReceiver {
using ERC165Checker for address;

/// @notice Address of the contract that is allowed to call handlePostTokenRebase.
address public immutable AUTHORIZED_REBASE_CALLER;

/// @notice Maximum amount of observers to be supported.
uint256 public constant MAX_OBSERVERS_COUNT = 32;

Expand All @@ -40,11 +43,16 @@ contract TokenRateNotifier is Ownable, IPostTokenRebaseReceiver {
address[] public observers;

/// @param initialOwner_ initial owner
constructor(address initialOwner_) {
/// @param authorizedRebaseCaller_ Address of the contract that is allowed to call handlePostTokenRebase.
constructor(address initialOwner_, address authorizedRebaseCaller_) {
if (initialOwner_ == address(0)) {
revert ErrorZeroAddressOwner();
}
if (authorizedRebaseCaller_ == address(0)) {
revert ErrorZeroAddressCaller();
}
_transferOwnership(initialOwner_);
AUTHORIZED_REBASE_CALLER = authorizedRebaseCaller_;
}

/// @notice Add a `observer_` to the back of array
Expand Down Expand Up @@ -94,6 +102,9 @@ contract TokenRateNotifier is Ownable, IPostTokenRebaseReceiver {
uint256, /* postTotalEther */
uint256 /* sharesMintedAsFees */
) external {
if (msg.sender != AUTHORIZED_REBASE_CALLER) {
revert ErrorNotAuthorizedRebaseCaller();
}
uint256 cachedObserversLength = observers.length;
for (uint256 obIndex = 0; obIndex < cachedObserversLength; obIndex++) {
// solhint-disable-next-line no-empty-blocks
Expand Down Expand Up @@ -141,5 +152,7 @@ contract TokenRateNotifier is Ownable, IPostTokenRebaseReceiver {
error ErrorMaxObserversCountExceeded();
error ErrorNoObserverToRemove();
error ErrorZeroAddressOwner();
error ErrorZeroAddressCaller();
error ErrorNotAuthorizedRebaseCaller();
error ErrorAddExistedObserver();
}
83 changes: 0 additions & 83 deletions scripts/optimism/deploy-bridge.ts

This file was deleted.

5 changes: 3 additions & 2 deletions scripts/optimism/deploy-scratch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import prompt from "../../utils/prompt";
import network from "../../utils/network";
import deployment from "../../utils/deployment";
import { BridgingManagement } from "../../utils/bridging-management";
import deploymentAllFromScratch from "../../utils/optimism/deployment";
import deploymentAll from "../../utils/optimism/deployment";

async function main() {
const networkName = env.network();
Expand All @@ -21,13 +21,14 @@ async function main() {

const deploymentConfig = deployment.loadMultiChainDeploymentConfig();

const [l1DeployScript, l2DeployScript] = await deploymentAllFromScratch (networkName, { logger: console })
const [l1DeployScript, l2DeployScript] = await deploymentAll (networkName, { logger: console })
.deployAllScript(
{
l1TokenNonRebasable: deploymentConfig.l1TokenNonRebasable,
l1TokenRebasable: deploymentConfig.l1RebasableToken,
accountingOracle: deploymentConfig.accountingOracle,
l2GasLimitForPushingTokenRate: deploymentConfig.l2GasLimitForPushingTokenRate,
l1AuthorizedRebaseCaller: deploymentConfig.l1AuthorizedRebaseCaller,

deployer: ethDeployer,
admins: {
Expand Down
60 changes: 43 additions & 17 deletions test/optimism/TokenRateNotifier.unit.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,23 @@ import {
unit("TokenRateNotifier", ctxFactory)

.test("deploy with zero address owner", async (ctx) => {
const { deployer } = ctx.accounts;
const { deployer, l1AuthorizedRebaseCaller } = ctx.accounts;

await assert.revertsWith(
new TokenRateNotifier__factory(deployer).deploy(ethers.constants.AddressZero),
new TokenRateNotifier__factory(deployer).deploy(ethers.constants.AddressZero, l1AuthorizedRebaseCaller.address),
"ErrorZeroAddressOwner()"
);
})

.test("deploy with zero address rebase caller", async (ctx) => {
const { deployer } = ctx.accounts;

await assert.revertsWith(
new TokenRateNotifier__factory(deployer).deploy(deployer.address, ethers.constants.AddressZero),
"ErrorZeroAddressCaller()"
);
})

.test("initial state", async (ctx) => {
const { tokenRateNotifier } = ctx.contracts;

Expand Down Expand Up @@ -63,9 +72,9 @@ unit("TokenRateNotifier", ctxFactory)

.test("addObserver() :: revert on adding observer with bad interface", async (ctx) => {
const { tokenRateNotifier } = ctx.contracts;
const { deployer } = ctx.accounts;
const { deployer, l1AuthorizedRebaseCaller } = ctx.accounts;

const observer = await new TokenRateNotifier__factory(deployer).deploy(deployer.address);
const observer = await new TokenRateNotifier__factory(deployer).deploy(deployer.address, l1AuthorizedRebaseCaller.address);
await assert.revertsWith(
tokenRateNotifier
.connect(ctx.accounts.owner)
Expand All @@ -76,7 +85,7 @@ unit("TokenRateNotifier", ctxFactory)

.test("addObserver() :: revert on adding too many observers", async (ctx) => {
const { tokenRateNotifier, opStackTokenRatePusher } = ctx.contracts;
const { deployer, owner, tokenRateOracle } = ctx.accounts;
const { deployer, owner, tokenRateOracle, l1AuthorizedRebaseCaller } = ctx.accounts;
const { l2GasLimitForPushingTokenRate, tokenRate, totalPooledEther, totalShares, genesisTime, secondsPerSlot, lastProcessingRefSlot } = ctx.constants;

assert.equalBN(await tokenRateNotifier.observersLength(), 0);
Expand All @@ -95,7 +104,8 @@ unit("TokenRateNotifier", ctxFactory)
deployer,
owner,
tokenRateOracle,
l2GasLimitForPushingTokenRate
l2GasLimitForPushingTokenRate,
l1AuthorizedRebaseCaller
);

await tokenRateNotifier
Expand Down Expand Up @@ -183,31 +193,41 @@ unit("TokenRateNotifier", ctxFactory)
assert.equalBN(await tokenRateNotifier.observersLength(), 0);
})

.test("handlePostTokenRebase() :: unauthorized caller", async (ctx) => {
const { tokenRateNotifier } = ctx.contracts;
const { stranger } = ctx.accounts;

await assert.revertsWith(
tokenRateNotifier.connect(stranger).handlePostTokenRebase(1, 2, 3, 4, 5, 6, 7),
"ErrorNotAuthorizedRebaseCaller()"
);
})

.test("handlePostTokenRebase() :: failed with some error", async (ctx) => {
const { tokenRateNotifier } = ctx.contracts;
const { deployer } = ctx.accounts;
const { deployer, l1AuthorizedRebaseCaller } = ctx.accounts;

const observer = await new OpStackTokenRatePusherWithSomeErrorStub__factory(deployer).deploy();
await tokenRateNotifier
.connect(ctx.accounts.owner)
.addObserver(observer.address);

const tx = await tokenRateNotifier.handlePostTokenRebase(1, 2, 3, 4, 5, 6, 7);
const tx = await tokenRateNotifier.connect(l1AuthorizedRebaseCaller).handlePostTokenRebase(1, 2, 3, 4, 5, 6, 7);

await assert.emits(tokenRateNotifier, tx, "PushTokenRateFailed", [observer.address, "0x332e27d2"]);
})

.test("handlePostTokenRebase() :: revert when observer has out of gas error", async (ctx) => {
const { tokenRateNotifier } = ctx.contracts;
const { deployer } = ctx.accounts;
const { deployer, l1AuthorizedRebaseCaller } = ctx.accounts;

const observer = await new OpStackTokenRatePusherWithOutOfGasErrorStub__factory(deployer).deploy();
await tokenRateNotifier
.connect(ctx.accounts.owner)
.addObserver(observer.address);

await assert.revertsWith(
tokenRateNotifier.handlePostTokenRebase(1, 2, 3, 4, 5, 6, 7),
tokenRateNotifier.connect(l1AuthorizedRebaseCaller).handlePostTokenRebase(1, 2, 3, 4, 5, 6, 7),
"ErrorTokenRateNotifierRevertedWithNoData()"
);
})
Expand All @@ -218,15 +238,15 @@ unit("TokenRateNotifier", ctxFactory)
l1MessengerStub,
opStackTokenRatePusher
} = ctx.contracts;
const { tokenRateOracle } = ctx.accounts;
const { tokenRateOracle, l1AuthorizedRebaseCaller } = ctx.accounts;
const { l2GasLimitForPushingTokenRate, tokenRate, genesisTime, secondsPerSlot, lastProcessingRefSlot } = ctx.constants;

const updateRateTime = genesisTime.add(secondsPerSlot.mul(lastProcessingRefSlot));

await tokenRateNotifier
.connect(ctx.accounts.owner)
.addObserver(opStackTokenRatePusher.address);
let tx = await tokenRateNotifier.handlePostTokenRebase(1, 2, 3, 4, 5, 6, 7);
let tx = await tokenRateNotifier.connect(l1AuthorizedRebaseCaller).handlePostTokenRebase(1, 2, 3, 4, 5, 6, 7);

await assert.emits(l1MessengerStub, tx, "SentMessage", [
tokenRateOracle.address,
Expand All @@ -246,7 +266,7 @@ unit("TokenRateNotifier", ctxFactory)
.run();

async function ctxFactory() {
const [deployer, owner, stranger, tokenRateOracle] = await ethers.getSigners();
const [deployer, owner, stranger, tokenRateOracle, l1AuthorizedRebaseCaller] = await ethers.getSigners();
const totalPooledEther = BigNumber.from('9309904612343950493629678');
const totalShares = BigNumber.from('7975822843597609202337218');
const tokenRateDecimals = BigNumber.from(27);
Expand All @@ -270,15 +290,17 @@ async function ctxFactory() {
deployer,
owner,
tokenRateOracle,
l2GasLimitForPushingTokenRate
l2GasLimitForPushingTokenRate,
l1AuthorizedRebaseCaller
);

return {
accounts: {
deployer,
owner,
stranger,
tokenRateOracle
tokenRateOracle,
l1AuthorizedRebaseCaller
},
contracts: {
tokenRateNotifier,
Expand Down Expand Up @@ -307,9 +329,13 @@ async function createContracts(
deployer: SignerWithAddress,
owner: SignerWithAddress,
tokenRateOracle: SignerWithAddress,
l2GasLimitForPushingTokenRate: number) {
l2GasLimitForPushingTokenRate: number,
l1AuthorizedRebaseCaller: SignerWithAddress) {

const tokenRateNotifier = await new TokenRateNotifier__factory(deployer).deploy(owner.address);
const tokenRateNotifier = await new TokenRateNotifier__factory(deployer).deploy(
owner.address,
l1AuthorizedRebaseCaller.address
);

const l1MessengerStub = await new CrossDomainMessengerStub__factory(deployer)
.deploy({ value: wei.toBigNumber(wei`1 ether`) });
Expand Down
16 changes: 11 additions & 5 deletions test/optimism/pushingTokenRate.integration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ import {
OptimismBridgeExecutor__factory,
TokenRateNotifier__factory,
TokenRateOracle__factory,
AccountingOracleStub__factory
AccountingOracleStub__factory,
EmptyContractStub__factory
} from "../../typechain";

scenario("Optimism :: Token Rate Oracle integration test", ctxFactory)
Expand All @@ -30,13 +31,12 @@ scenario("Optimism :: Token Rate Oracle integration test", ctxFactory)
genesisTime,
secondsPerSlot,
lastProcessingRefSlot,
tokenRate
tokenRate,
l1AuthorizedRebaseCaller
} = ctx;

const account = ctx.accounts.accountA;

const tx = await tokenRateNotifier
.connect(account.l1Signer)
.connect(l1AuthorizedRebaseCaller)
.handlePostTokenRebase(1, 2, 3, 4, 5, 6, 7);

const messageNonce = await l1CrossDomainMessenger.messageNonce();
Expand Down Expand Up @@ -169,6 +169,10 @@ async function ctxFactory() {

const [l2ERC20TokenBridge] = await hre.ethers.getSigners();

const l1AuthorizedRebaseCaller = await new EmptyContractStub__factory(l1Deployer).deploy({ value: 10000000 });
const l1AuthorizedRebaseCallerAsEOA = await testing.impersonate(l1AuthorizedRebaseCaller.address);
await testing.setBalance(l1AuthorizedRebaseCaller.address, wei.toBigNumber(wei`1 ether`));

const [ethDeployScript, optDeployScript] = await deploymentOracle(
networkName
).oracleDeployScript(
Expand All @@ -178,6 +182,7 @@ async function ctxFactory() {
l2GasLimitForPushingTokenRate,
tokenRateOutdatedDelay,
{
l1AuthorizedRebaseCaller: l1AuthorizedRebaseCaller.address,
deployer: l1Deployer,
admins: {
proxy: l1Deployer.address,
Expand Down Expand Up @@ -245,6 +250,7 @@ async function ctxFactory() {
blockTimestamp,
tokenRate,
genesisTime, secondsPerSlot, lastProcessingRefSlot,
l1AuthorizedRebaseCaller: l1AuthorizedRebaseCallerAsEOA,
accounts: {
accountA,
l1CrossDomainMessengerAliased
Expand Down
2 changes: 2 additions & 0 deletions utils/deployment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ interface MultiChainDeploymentConfig {
accountingOracle: string;
l2GasLimitForPushingTokenRate: BigNumber;
l1TokenBridge: string;
l1AuthorizedRebaseCaller: string;

/// L2
/// Oracle
Expand Down Expand Up @@ -46,6 +47,7 @@ export function loadMultiChainDeploymentConfig(): MultiChainDeploymentConfig {
accountingOracle: env.address("ACCOUNTING_ORACLE"),
l2GasLimitForPushingTokenRate: BigNumber.from(env.string("L2_GAS_LIMIT_FOR_PUSHING_TOKEN_RATE")),
l1TokenBridge: env.address("L1_TOKEN_BRIDGE"),
l1AuthorizedRebaseCaller: env.address("L1_AUTHORIZED_REBASE_CALLER"),

/// L2 Part
/// TokenRateOracle
Expand Down
Loading

0 comments on commit 12333ef

Please sign in to comment.