Skip to content

Commit

Permalink
feat: bridge contract migrate (PundiAI#238)
Browse files Browse the repository at this point in the history
  • Loading branch information
todd-woko authored Mar 26, 2024
1 parent b00528b commit cc7970e
Show file tree
Hide file tree
Showing 5 changed files with 277 additions and 9 deletions.
41 changes: 41 additions & 0 deletions solidity/contracts/bridge/FxBridgeMigrateLogic.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// SPDX-License-Identifier: MIT

pragma experimental ABIEncoderV2;
pragma solidity ^0.8.0;

import {FxBridgeLogic} from "./FxBridgeLogic.sol";

/* solhint-disable custom-errors */
contract FxBridgeMigrateLogic is FxBridgeLogic {
/* =============== MIGRATE =============== */
function migrateInit(
bytes32 _fxBridgeId,
uint256 _powerThreshold,
uint256 _lastEventNonce,
bytes32 _lastOracleSetCheckpoint,
uint256 _lastOracleSetNonce,
address[] memory _bridgeTokens,
uint256[] memory _lastBatchNonces,
TokenStatus[] memory _tokenStatuses
) public initializer {
__Pausable_init();
__Ownable_init();
__ReentrancyGuard_init();
require(
_lastBatchNonces.length == _bridgeTokens.length &&
_bridgeTokens.length == _tokenStatuses.length,
"Malformed last batch token information."
);
state_fxBridgeId = _fxBridgeId;
state_powerThreshold = _powerThreshold;
state_lastEventNonce = _lastEventNonce;
state_lastOracleSetCheckpoint = _lastOracleSetCheckpoint;
state_lastOracleSetNonce = _lastOracleSetNonce;
for (uint256 i = 0; i < _bridgeTokens.length; i++) {
bridgeTokens.push(_bridgeTokens[i]);
state_lastBatchNonces[_bridgeTokens[i]] = _lastBatchNonces[i];
tokenStatus[_bridgeTokens[i]] = _tokenStatuses[i];
}
version = "1.0.0";
}
}
41 changes: 41 additions & 0 deletions solidity/contracts/bridge/FxBridgeMigrateLogicETH.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// SPDX-License-Identifier: Apache-2.0

pragma experimental ABIEncoderV2;
pragma solidity ^0.8.0;

import {FxBridgeLogicETH} from "./FxBridgeLogicETH.sol";

/* solhint-disable custom-errors */
contract FxBridgeMigrateLogicETH is FxBridgeLogicETH {
/* =============== MIGRATE =============== */
function migrateInit(
bytes32 _fxBridgeId,
uint256 _powerThreshold,
uint256 _lastEventNonce,
bytes32 _lastOracleSetCheckpoint,
uint256 _lastOracleSetNonce,
address[] memory _bridgeTokens,
uint256[] memory _lastBatchNonces,
TokenStatus[] memory _tokenStatuses
) public initializer {
__Pausable_init();
__Ownable_init();
__ReentrancyGuard_init();
require(
_lastBatchNonces.length == _bridgeTokens.length &&
_bridgeTokens.length == _tokenStatuses.length,
"Malformed last batch token information."
);
state_fxBridgeId = _fxBridgeId;
state_powerThreshold = _powerThreshold;
state_lastEventNonce = _lastEventNonce;
state_lastOracleSetCheckpoint = _lastOracleSetCheckpoint;
state_lastOracleSetNonce = _lastOracleSetNonce;
for (uint256 i = 0; i < _bridgeTokens.length; i++) {
bridgeTokens.push(_bridgeTokens[i]);
state_lastBatchNonces[_bridgeTokens[i]] = _lastBatchNonces[i];
tokenStatus[_bridgeTokens[i]] = _tokenStatuses[i];
}
version = "1.0.0";
}
}
36 changes: 28 additions & 8 deletions solidity/hardhat.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,25 @@ const config: HardhatUserConfig = {
networks: {
hardhat: {
// forking: {
// url: `${process.env.MAINNET_URL || "https://mainnet.infura.io/v3/infura-key"}`,
// url: `${process.env.MAINNET_URL || "https://mainnet.infura.io/v3/"+process.env.INFURA_KEY}`,
// }
chainId: 1337,
},
mainnet: {
url: `${process.env.MAINNET_URL || "https://mainnet.infura.io/v3/infura-key"}`,
url: `${process.env.MAINNET_URL || "https://mainnet.infura.io/v3/"+process.env.INFURA_KEY}`,
chainId: 1,
},
goerli: {
url: `${process.env.GOERLI_URL || "https://goerli.infura.io/v3/infura-key"}`,
chainId: 5,
sepolia: {
url: `${process.env.SEPOLIA_URL || "https://sepolia.infura.io/v3/"+process.env.INFURA_KEY}`,
chainId: 11155111,
},
arbitrumSepolia: {
url: `${process.env.ARBITRUM_URL || "https://sepolia-rollup.arbitrum.io/rpc"}`,
chainId: 421614,
},
optimisticSepolia: {
url: `${process.env.OPTIMISTIC_URL || "https://sepolia.optimism.io"}`,
chainId: 11155420,
},
localhost: {
url: `${process.env.LOCAL_URL || "http://127.0.0.1:8545"}`,
Expand Down Expand Up @@ -61,9 +69,21 @@ const config: HardhatUserConfig = {
},
etherscan: {
apiKey: {
mainnet: `${process.env.ETHERSCAN_API_KEY || "scan-key"}`,
goerli: `${process.env.ETHERSCAN_API_KEY || "scan-key"}`,
}
mainnet: `${process.env.ETHERSCAN_API_KEY}`,
sepolia: `${process.env.ETHERSCAN_API_KEY}`,
arbitrumSepolia: `${process.env.ETHERSCAN_API_KEY}`,
optimisticSepolia: `${process.env.ETHERSCAN_API_KEY}`,
},
customChains: [
{
network: "optimisticSepolia",
chainId: 11155420,
urls: {
apiURL: "https://api-sepolia-optimistic.etherscan.io/api",
browserURL: "https://sepolia-optimism.etherscan.io/"
}
}
]
},
dependencyCompiler: {
paths: [
Expand Down
145 changes: 144 additions & 1 deletion solidity/tasks/contract_task.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ import {task} from "hardhat/config";
import {string} from "hardhat/internal/core/params/argumentTypes";
import {
AddTxParam,
BridgeStateInfoToJson,
SUB_CHECK_PRIVATE_KEY,
SUB_CONFIRM_TRANSACTION,
SUB_CREATE_TRANSACTION,
SUB_GET_CONTRACT_ADDR,
TransactionToJson
} from "./subtasks";
import {FxBridgeLogic, IFxBridgeLogic} from "../typechain-types";

const deploy = task("deploy-contract", "deploy contract")
.addParam("contractName", "deploy contract name", undefined, string, false)
Expand Down Expand Up @@ -47,4 +49,145 @@ const deploy = task("deploy-contract", "deploy contract")
}
});

AddTxParam([deploy])
const migrateBridge = task("migrate-bridge", "migrate bridge")
.addParam("oldBridge", "old bridge address", undefined, string, false)
.addParam("oldRpc", "old rpc url", undefined, string, false)
.addParam("newBridgeProxy", "new bridge proxy address", undefined, string, true)
.addParam("newBridgeLogic", "new bridge logic address", undefined, string, true)
.addParam("admin", "admin address", undefined, string, true)
.setAction(async (taskArgs, hre) => {
let {oldBridge, oldRpc, newBridgeProxy, newBridgeLogic, admin} = taskArgs
const provider = new hre.ethers.JsonRpcProvider(oldRpc);
const oldBridgeContract = await hre.ethers.getContractAt("FxBridgeLogic", oldBridge) as FxBridgeLogic;
const fxBridgeId = await oldBridgeContract.connect(provider).state_fxBridgeId()
const powerThreshold = await oldBridgeContract.connect(provider).state_powerThreshold()
const lastEventNonce = await oldBridgeContract.connect(provider).state_lastEventNonce()
const lastOracleSetNonce = await oldBridgeContract.connect(provider).state_lastOracleSetNonce()
const lastOracleSetCheckpoint = await oldBridgeContract.connect(provider).state_lastOracleSetCheckpoint()
const bridgeTokens = await oldBridgeContract.connect(provider).getBridgeTokenList()

let bridgeTokenAddress: string[] = [];
let lastBatchNonce: bigint[] = [];
let tokenStatus: IFxBridgeLogic.TokenStatusStruct[] = [];

for (let i = 0; i < bridgeTokens.length; i++) {
const token = bridgeTokens[i];
bridgeTokenAddress.push(token.addr);
const batchNonce = await oldBridgeContract.connect(provider).state_lastBatchNonces(token.addr)
lastBatchNonce.push(batchNonce);
const status = await oldBridgeContract.connect(provider).tokenStatus(token.addr)
tokenStatus.push(status);
}

const bridgeMigrateLogicFactory: any = await hre.ethers.getContractFactory("FxBridgeMigrateLogic")
let proxyFactory: any = await hre.ethers.getContractFactory("TransparentUpgradeableProxy")
const initData = bridgeMigrateLogicFactory.interface.encodeFunctionData('migrateInit', [
fxBridgeId,
powerThreshold,
lastEventNonce,
lastOracleSetCheckpoint,
lastOracleSetNonce,
bridgeTokenAddress,
lastBatchNonce,
tokenStatus
])
let {answer} = await hre.run(SUB_CONFIRM_TRANSACTION, {
message: `\n${BridgeStateInfoToJson(
fxBridgeId,
powerThreshold,
lastEventNonce,
lastOracleSetNonce,
lastOracleSetCheckpoint,
bridgeTokenAddress,
lastBatchNonce,
tokenStatus
)}\n`,
disableConfirm: taskArgs.disableConfirm,
});
if (!answer) return;

const {wallet} = await hre.run(SUB_CHECK_PRIVATE_KEY, taskArgs);
if (!newBridgeLogic) {
const paramData = bridgeMigrateLogicFactory.interface.encodeDeploy([]);
const data = bridgeMigrateLogicFactory.bytecode + paramData.slice(2);
const tx = await hre.run(SUB_CREATE_TRANSACTION, {
from: await wallet.getAddress(), data: data,
gasPrice: taskArgs.gasPrice,
maxFeePerGas: taskArgs.maxFeePerGas,
maxPriorityFeePerGas: taskArgs.maxPriorityFeePerGas,
nonce: taskArgs.nonce,
gasLimit: taskArgs.gasLimit,
});

answer = await hre.run(SUB_CONFIRM_TRANSACTION, {
message: `\n${TransactionToJson(tx)}\n`,
disableConfirm: taskArgs.disableConfirm,
});
if (!answer) return;

try {
const deployTx = await wallet.sendTransaction(tx);
const receipt = await deployTx.wait();
newBridgeLogic = receipt.contractAddress;
console.log(`deploy bridge logic, ${newBridgeLogic}`)
} catch (e) {
console.log(`Deploy failed, ${e}`)
return;
}
}
admin = admin || await wallet.getAddress()
if (!newBridgeProxy) {
const paramData = proxyFactory.interface.encodeDeploy([newBridgeLogic, admin, initData]);
const data = proxyFactory.bytecode + paramData.slice(2);
const tx = await hre.run(SUB_CREATE_TRANSACTION, {
from: await wallet.getAddress(), data: data,
gasPrice: taskArgs.gasPrice,
maxFeePerGas: taskArgs.maxFeePerGas,
maxPriorityFeePerGas: taskArgs.maxPriorityFeePerGas,
nonce: taskArgs.nonce,
gasLimit: taskArgs.gasLimit,
});

answer = await hre.run(SUB_CONFIRM_TRANSACTION, {
message: `\n${TransactionToJson(tx)}\n`,
disableConfirm: taskArgs.disableConfirm,
});
if (!answer) return;

try {
const deployTx = await wallet.sendTransaction(tx);
const receipt = await deployTx.wait();
newBridgeProxy = receipt.contractAddress;
console.log(`deploy proxy, ${newBridgeProxy}`)
} catch (e) {
console.log(`Deploy failed, ${e}`)
return;
}
} else {
proxyFactory = await hre.ethers.getContractAt("ITransparentUpgradeableProxy", newBridgeProxy, wallet)
const data = proxyFactory.interface.encodeFunctionData('upgradeToAndCall', [newBridgeLogic, initData])
const tx = await hre.run(SUB_CREATE_TRANSACTION, {
from: await wallet.getAddress(), to: newBridgeProxy, data: data,
gasPrice: taskArgs.gasPrice,
maxFeePerGas: taskArgs.maxFeePerGas,
maxPriorityFeePerGas: taskArgs.maxPriorityFeePerGas,
nonce: taskArgs.nonce,
gasLimit: taskArgs.gasLimit,
});

answer = await hre.run(SUB_CONFIRM_TRANSACTION, {
message: `\n${TransactionToJson(tx)}\n`,
disableConfirm: taskArgs.disableConfirm,
});
if (!answer) return;

try {
const migrateTx = await wallet.sendTransaction(tx);
await migrateTx.wait();
console.log(`migrate success, ${migrateTx.hash}`)
} catch (e) {
console.log(`migrate failed, ${e}`)
}
}
})
AddTxParam([deploy, migrateBridge])
23 changes: 23 additions & 0 deletions solidity/tasks/subtasks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {ConfigurableTaskDefinition} from "hardhat/types";
import {boolean, string} from "hardhat/internal/core/params/argumentTypes";
import TransportNodeHid from "@ledgerhq/hw-transport-node-hid";
import inquirer from 'inquirer';
import {IFxBridgeLogic} from "../typechain-types";

// sub task name
export const SUB_CHECK_PRIVATE_KEY: string = "sub:check-private-key";
Expand Down Expand Up @@ -239,6 +240,28 @@ export function TransactionToJson(transaction: TransactionLike): string {
}, null, 2);
}

export function BridgeStateInfoToJson(
fxBridgeId: string,
powerThreshold: bigint,
lastEventNonce: bigint,
lastOracleSetNonce: bigint,
lastOracleSetCheckpoint: string,
bridgeTokenAddress: string[],
lastBatchNonce: bigint[],
tokenStatus: IFxBridgeLogic.TokenStatusStruct[]
): string {
return JSON.stringify({
fxBridgeId: fxBridgeId,
powerThreshold: powerThreshold.toString(),
lastEventNonce: lastEventNonce.toString(),
lastOracleSetNonce: lastOracleSetNonce.toString(),
lastOracleSetCheckpoint: lastOracleSetCheckpoint,
bridgeTokenAddress: bridgeTokenAddress.toString(),
lastBatchNonce: lastBatchNonce.toString(),
tokenStatus: tokenStatus.toString()
}, null, 2);
}

export const vote_power = 2834678415

type Oracle = {
Expand Down

0 comments on commit cc7970e

Please sign in to comment.