Skip to content

Commit dfae8a3

Browse files
Plume Repo Setup (#2485)
* Repo setup for Plume * add provider URL config * switch to the legacy testnet that has the rooster AMM deployed * [Ethereum][Plume] Deploy LayerZero Adapter for wOETH (#2486) * Layerzero bridge stuff * Revert change and add tooling * Update OmnichainL2Adapter * Cleanup * Fix WOETHPlume * Wait for block confirmation * Fix slither on Vault * Plume Vault and Token deploy (#2489) * Layerzero bridge stuff * Revert change and add tooling * Update OmnichainL2Adapter * Cleanup * Fix WOETHPlume * Wait for block confirmation * Fix slither on Vault * cleanup code * Tweaks * Also, add a temp dripper * Fix tests * Deploy core contracts * Exclude layerzero from slither --------- Co-authored-by: Domen Grabec <[email protected]>
1 parent 0161701 commit dfae8a3

File tree

71 files changed

+22127
-148
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

71 files changed

+22127
-148
lines changed

.github/workflows/defi.yml

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,46 @@ jobs:
294294
./contracts/coverage.json
295295
./contracts/coverage/**/*
296296
retention-days: 1
297+
298+
contracts-plume-forktest:
299+
name: "Plume Fork Tests"
300+
runs-on: ubuntu-latest
301+
continue-on-error: true
302+
env:
303+
HARDHAT_CACHE_DIR: ./cache
304+
PROVIDER_URL: ${{ secrets.PROVIDER_URL }}
305+
PLUME_PROVIDER_URL: ${{ secrets.PLUME_PROVIDER_URL }}
306+
steps:
307+
- uses: actions/checkout@v4
308+
309+
- name: Use Node.js
310+
uses: actions/setup-node@v4
311+
with:
312+
node-version: "20.x"
313+
cache: "yarn"
314+
cache-dependency-path: contracts/yarn.lock
315+
316+
- uses: actions/cache@v4
317+
id: hardhat-cache
318+
with:
319+
path: contracts/cache
320+
key: ${{ runner.os }}-hardhat-${{ hashFiles('contracts/cache/*.json') }}
321+
restore-keys: |
322+
${{ runner.os }}-hardhat-cache
323+
324+
- run: yarn install --frozen-lockfile
325+
working-directory: ./contracts
326+
327+
- run: yarn run test:coverage:plume-fork
328+
working-directory: ./contracts
329+
330+
- uses: actions/upload-artifact@v4
331+
with:
332+
name: fork-test-plume-coverage-${{ github.sha }}
333+
path: |
334+
./contracts/coverage.json
335+
./contracts/coverage/**/*
336+
retention-days: 1
297337

298338
coverage-uploader:
299339
name: "Upload Coverage Reports"
@@ -306,6 +346,7 @@ jobs:
306346
- contracts-arb-forktest
307347
- contracts-base-forktest
308348
- contracts-sonic-forktest
349+
- contracts-plume-forktest
309350
env:
310351
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
311352
steps:
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
// SPDX-License-Identifier: BUSL-1.1
2+
3+
pragma solidity ^0.8.0;
4+
5+
import { MintBurnOFTAdapter } from "@layerzerolabs/oft-evm/contracts/MintBurnOFTAdapter.sol";
6+
import { IMintableBurnable } from "@layerzerolabs/oft-evm/contracts/interfaces/IMintableBurnable.sol";
7+
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
8+
9+
/// NOTE: It's necessary to inherit from Ownable instead of Governable
10+
/// because OFTCore uses Ownable to manage the governor.
11+
/// Ownable uses slot 0 for storing the address, whereas Goveranble
12+
/// stores it in a computed slot.
13+
14+
/// @notice Omnichain uses a deployed ERC-20 token and safeERC20
15+
/// to interact with the OFTCore contract.
16+
/// On L2, we follow the mint and burn mechanism.
17+
/// The adapter should have minter and burner roles.
18+
19+
/// @title Omnichain L2 Adapter
20+
contract OmnichainL2Adapter is MintBurnOFTAdapter {
21+
constructor(
22+
address _token,
23+
address _lzEndpoint,
24+
address _governor
25+
)
26+
MintBurnOFTAdapter(
27+
_token,
28+
IMintableBurnable(_token),
29+
_lzEndpoint,
30+
_governor
31+
)
32+
Ownable()
33+
{
34+
_transferOwnership(_governor);
35+
}
36+
37+
/// @inheritdoc MintBurnOFTAdapter
38+
function _debit(
39+
address _from,
40+
uint256 _amountLD,
41+
uint256 _minAmountLD,
42+
uint32 _dstEid
43+
)
44+
internal
45+
virtual
46+
override
47+
returns (uint256 amountSentLD, uint256 amountReceivedLD)
48+
{
49+
(amountSentLD, amountReceivedLD) = _debitView(
50+
_amountLD,
51+
_minAmountLD,
52+
_dstEid
53+
);
54+
// Burns tokens from the caller.
55+
IMintableERC20(address(minterBurner)).burn(_from, amountSentLD);
56+
}
57+
58+
/// @inheritdoc MintBurnOFTAdapter
59+
function _credit(
60+
address _to,
61+
uint256 _amountLD,
62+
uint32 /* _srcEid */
63+
) internal virtual override returns (uint256 amountReceivedLD) {
64+
if (_to == address(0x0)) _to = address(0xdead); // _mint(...) does not support address(0x0)
65+
// Mints the tokens and transfers to the recipient.
66+
IMintableERC20(address(minterBurner)).mint(_to, _amountLD);
67+
// In the case of NON-default OFTAdapter, the amountLD MIGHT not be equal to amountReceivedLD.
68+
return _amountLD;
69+
}
70+
}
71+
72+
interface IMintableERC20 {
73+
function mint(address to, uint256 value) external;
74+
75+
function burn(address to, uint256 value) external;
76+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// SPDX-License-Identifier: BUSL-1.1
2+
3+
pragma solidity ^0.8.0;
4+
5+
import { OFTAdapter } from "@layerzerolabs/oft-evm/contracts/OFTAdapter.sol";
6+
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
7+
8+
/// NOTE: It's necessary to inherit from Ownable instead of Governable
9+
/// because OFTCore uses Ownable to manage the governor.
10+
/// Ownable uses slot 0 for storing the address, whereas Goveranble
11+
/// stores it in a computed slot.
12+
13+
/// @notice Omnichain uses a deployed ERC-20 token and safeERC20
14+
/// to interact with the OFTCore contract.
15+
/// On Ethereum, we follow the lock and unlock mechanism.
16+
17+
/// @title Omnichain Mainnet Adapter
18+
contract OmnichainMainnetAdapter is OFTAdapter {
19+
constructor(
20+
address _token,
21+
address _lzEndpoint,
22+
address _governor
23+
) OFTAdapter(_token, _lzEndpoint, _governor) Ownable() {
24+
_transferOwnership(_governor);
25+
}
26+
}

contracts/contracts/interfaces/IVault.sol

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ pragma solidity ^0.8.0;
44
import { VaultStorage } from "../vault/VaultStorage.sol";
55

66
interface IVault {
7+
// slither-disable-start constable-states
8+
79
event AssetSupported(address _asset);
810
event AssetDefaultStrategyUpdated(address _asset, address _strategy);
911
event AssetAllocated(address _asset, address _strategy, uint256 _amount);
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.0;
3+
4+
import { InitializeGovernedUpgradeabilityProxy } from "./InitializeGovernedUpgradeabilityProxy.sol";
5+
6+
/**
7+
* @notice OETHPlumeVaultProxy delegates calls to OETHPlumeVault implementation
8+
*/
9+
contract OETHPlumeVaultProxy is InitializeGovernedUpgradeabilityProxy {
10+
11+
}
12+
13+
/**
14+
* @notice OETHPlumeProxy delegates calls to OETH implementation
15+
*/
16+
contract OETHPlumeProxy is InitializeGovernedUpgradeabilityProxy {
17+
18+
}
19+
20+
/**
21+
* @notice WOETHPlumeProxy delegates calls to WOETH implementation
22+
*/
23+
contract WOETHPlumeProxy is InitializeGovernedUpgradeabilityProxy {
24+
25+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.0;
3+
4+
import { OUSD } from "./OUSD.sol";
5+
6+
/**
7+
* @title Super OETH (Plume) Token Contract
8+
* @author Origin Protocol Inc
9+
*/
10+
contract OETHPlume is OUSD {
11+
constructor() {
12+
// Nobody owns the implementation contract
13+
_setGovernor(address(0));
14+
}
15+
16+
function symbol() external pure override returns (string memory) {
17+
return "superOETHp";
18+
}
19+
20+
function name() external pure override returns (string memory) {
21+
return "Super OETH";
22+
}
23+
24+
function decimals() external pure override returns (uint8) {
25+
return 18;
26+
}
27+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.0;
3+
4+
import { WOETH } from "./WOETH.sol";
5+
import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
6+
7+
/**
8+
* @title wOETH (Plume) Token Contract
9+
* @author Origin Protocol Inc
10+
*/
11+
12+
contract WOETHPlume is WOETH {
13+
constructor(ERC20 underlying_) WOETH(underlying_) {}
14+
15+
function name() public view virtual override returns (string memory) {
16+
return "Wrapped Super OETH";
17+
}
18+
19+
function symbol() public view virtual override returns (string memory) {
20+
return "wsuperOETHp";
21+
}
22+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
const {
2+
deploymentWithGovernanceProposal,
3+
deployWithConfirmation,
4+
} = require("../../utils/deploy");
5+
6+
const addresses = require("../../utils/addresses");
7+
8+
module.exports = deploymentWithGovernanceProposal(
9+
{
10+
deployName: "133_omnichain_adapter",
11+
},
12+
async ({ ethers }) => {
13+
const { deployerAddr } = await getNamedAccounts();
14+
15+
const cWOETHProxy = await ethers.getContract("WOETHProxy");
16+
17+
// NOTE: For now, deployer is the governor to test things out
18+
const dOmnichainMainnetAdapter = await deployWithConfirmation(
19+
"OmnichainMainnetAdapter",
20+
[cWOETHProxy.address, addresses.mainnet.LayerZeroEndpointV2, deployerAddr]
21+
);
22+
23+
console.log(
24+
"OmnichainMainnetAdapter address:",
25+
dOmnichainMainnetAdapter.address
26+
);
27+
28+
return {};
29+
}
30+
);

contracts/deploy/plume/000_mock.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
const { deployOnPlume } = require("../../utils/deploy-l2");
2+
const { deployWithConfirmation } = require("../../utils/deploy");
3+
const { replaceContractAt } = require("../../utils/hardhat");
4+
const addresses = require("../../utils/addresses");
5+
6+
module.exports = deployOnPlume(
7+
{
8+
deployName: "000_mock",
9+
forceSkip: true,
10+
},
11+
async () => {
12+
// Just a workaround to get WETH on testnet
13+
await deployWithConfirmation("MockWETH", []);
14+
const mockWETH = await ethers.getContract("MockWETH");
15+
await replaceContractAt(addresses.plume.WETH, mockWETH);
16+
17+
const weth = await ethers.getContractAt("MockWETH", addresses.plume.WETH);
18+
console.log("MockWETH live at", weth.address);
19+
}
20+
);

contracts/deploy/plume/001_woeth.js

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
const { deployOnPlume } = require("../../utils/deploy-l2");
2+
const {
3+
deployWithConfirmation,
4+
withConfirmation,
5+
} = require("../../utils/deploy");
6+
const { getTxOpts } = require("../../utils/tx");
7+
const addresses = require("../../utils/addresses");
8+
9+
module.exports = deployOnPlume(
10+
{
11+
deployName: "001_woeth",
12+
},
13+
async ({ ethers }) => {
14+
const { deployerAddr } = await getNamedAccounts();
15+
const sDeployer = await ethers.provider.getSigner(deployerAddr);
16+
17+
// Deploy Proxy
18+
await deployWithConfirmation("BridgedWOETHProxy", []);
19+
const cWOETHProxy = await ethers.getContract("BridgedWOETHProxy");
20+
console.log("BridgedWOETHProxy address:", cWOETHProxy.address);
21+
22+
// Deploy Bridged WOETH Token implementation
23+
await deployWithConfirmation("BridgedWOETH", []);
24+
const cWOETHImpl = await ethers.getContract("BridgedWOETH");
25+
console.log("BridgedWOETH address:", cWOETHImpl.address);
26+
27+
const cWOETH = await ethers.getContractAt(
28+
"BridgedWOETH",
29+
cWOETHProxy.address
30+
);
31+
32+
const initData = cWOETHImpl.interface.encodeFunctionData(
33+
"initialize()",
34+
[]
35+
);
36+
37+
// Initialize the proxy
38+
// prettier-ignore
39+
await withConfirmation(
40+
cWOETHProxy
41+
.connect(sDeployer)["initialize(address,address,bytes)"](
42+
cWOETHImpl.address,
43+
deployerAddr, // Pretend Deployer is Governor for now
44+
initData,
45+
await getTxOpts()
46+
)
47+
);
48+
console.log("Initialized BridgedWOETHProxy");
49+
50+
// Deploy OmnichainL2Adapter
51+
const dOmnichainL2Adapter = await deployWithConfirmation(
52+
"OmnichainL2Adapter",
53+
[cWOETHProxy.address, addresses.plume.LayerZeroEndpointV2, deployerAddr]
54+
);
55+
56+
console.log("OmnichainL2Adapter address:", dOmnichainL2Adapter.address);
57+
58+
// Grant permissions to the adapter
59+
await withConfirmation(
60+
cWOETH
61+
.connect(sDeployer)
62+
.grantRole(await cWOETHImpl.MINTER_ROLE(), dOmnichainL2Adapter.address)
63+
);
64+
65+
await withConfirmation(
66+
cWOETH
67+
.connect(sDeployer)
68+
.grantRole(await cWOETHImpl.BURNER_ROLE(), dOmnichainL2Adapter.address)
69+
);
70+
console.log("Granted minter & burner roles to OmnichainL2Adapter");
71+
72+
// Set Peer
73+
const cOmnichainL2Adapter = await ethers.getContract("OmnichainL2Adapter");
74+
75+
await withConfirmation(
76+
cOmnichainL2Adapter.connect(sDeployer).setPeer(
77+
// Ref: https://docs.layerzero.network/v2/deployments/deployed-contracts
78+
"30101", // Ethereum endpoint ID
79+
ethers.utils.zeroPad(addresses.mainnet.WOETHOmnichainAdapter, 32)
80+
)
81+
);
82+
console.log("Peer set for OmnichainL2Adapter");
83+
}
84+
);

0 commit comments

Comments
 (0)