Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -192,10 +192,6 @@ contract L2ETHBridgeVault is

ethAmountTracker = ethAmountTracker + depositAmount;

/// @notice Encoding the context to process the loan after the price is fetched
/// @dev The context contains the borrower’s details, loan amount, borrow token, and collateral token.
// bytes memory ethTransferCallbackContext = abi.encode("0x");

/// @notice Send a request to the token contract to get token minted.
/// @dev This request is processed with a fee for the transaction, allowing the system to fetch the token price.
sendRequest(depositRecipient, depositAmount, Nil.ASYNC_REQUEST_MIN_GAS, "", "", handleETHTransferResponse);
Expand Down
4 changes: 2 additions & 2 deletions rollup-bridge-contracts/hardhat.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import * as fs from "fs";
dotenv.config();

function getRemappings() {
const remappingsTxt = fs.readFileSync("remappings.txt", "utf8");
const remappingsTxt = fs.readFileSync(resolve(__dirname, "remappings.txt"), "utf8");
return remappingsTxt
.split("\n")
.filter((line) => line.trim() !== "")
Expand Down Expand Up @@ -97,7 +97,7 @@ const config: HardhatUserConfig = {
},
nil: {
url: process.env.NIL_RPC_ENDPOINT,
accounts: process.env.PRIVATE_KEY ? [process.env.PRIVATE_KEY] : [],
accounts: process.env.NIL_PRIVATE_KEY ? [process.env.NIL_PRIVATE_KEY] : [],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does PRIVATE_KEY environment variable used by any another scripts?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@akokoshn this is used in the function to create a new smart-account
we are in midst of getting rid of this variable and create new key-pair for unit tests

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this will be removed in the new PR coming up soon this week @akokoshn

},
},
namedAccounts: {
Expand Down
111 changes: 111 additions & 0 deletions rollup-bridge-contracts/task/nil-smart-account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
LocalECDSAKeySigner,
PublicClient,
SmartAccountV1,
Hex,
waitTillCompleted,
} from "@nilfoundation/niljs";
import "dotenv/config";
Expand Down Expand Up @@ -169,3 +170,113 @@ export async function generateNilSmartAccount(networkName: string): Promise<[Sma

return [smartAccount, depositRecipientSmartAccount];
}

export async function prepareNilSmartAccountsForUnitTest(): Promise<{
ownerSmartAccount: SmartAccountV1,
depositRecipientSmartAccount: SmartAccountV1,
feeRefundSmartAccount: SmartAccountV1
}> {
const rpcEndpoint = process.env.NIL_RPC_ENDPOINT as string;
const faucetEndpoint = process.env.FAUCET_ENDPOINT as string;

const faucetClient = new FaucetClient({
transport: new HttpTransport({ endpoint: faucetEndpoint }),
});

const client = new PublicClient({
transport: new HttpTransport({ endpoint: rpcEndpoint }),
});
// Generate a new ECDSA key pair
const owner_wallet = ethers.Wallet.createRandom();
let owner_privateKey = owner_wallet.privateKey;
console.log(`owner_privateKey is: ${owner_privateKey}`);
const owner_wallet_address = owner_wallet.address; // This is the public address

console.log("Generated private key:", owner_privateKey);
console.log("Generated address:", owner_wallet_address);
console.log(`preparing owner nil-smart-account`);

let ownerSmartAccount: SmartAccountV1 | null = null;

const owner_signer = new LocalECDSAKeySigner({ privateKey: owner_privateKey as `0x${string}` });

try {
ownerSmartAccount = new SmartAccountV1({
signer: owner_signer,
client: client,
salt: BigInt(Math.floor(Math.random() * 10000)),
shardId: 1,
pubkey: owner_signer.getPublicKey(),
});

await topupSmartAccount(faucetClient, client, ownerSmartAccount.address);
console.log("🆕 owner Smart Account Generated:", ownerSmartAccount.address);

if ((await ownerSmartAccount.checkDeploymentStatus()) === false) {
await ownerSmartAccount.selfDeploy(true);
}

console.log("🆕 owner Smart Account Generated:", ownerSmartAccount.address);
} catch (err) {
console.error(`failed to self-deploy owner-smart-account: ${err}`);
return;
}

const deposit_recipient_wallet = ethers.Wallet.createRandom();
const deposit_recipient_privateKey = deposit_recipient_wallet.privateKey; // This is a 0x... string
const deposit_recipient_wallet_address = deposit_recipient_wallet.address; // This is the public address

let deposit_recipient_signer = new LocalECDSAKeySigner({ privateKey: deposit_recipient_privateKey as Hex });
const depositRecipientSmartAccount = new SmartAccountV1({
signer: deposit_recipient_signer,
client,
salt: BigInt(Math.floor(Math.random() * 10000)),
shardId: 1,
pubkey: deposit_recipient_signer.getPublicKey(),
});
const depositRecipientSmartAccountAddress = depositRecipientSmartAccount.address;

await topupSmartAccount(faucetClient, client, depositRecipientSmartAccountAddress);

if ((await depositRecipientSmartAccount.checkDeploymentStatus()) === false) {
await depositRecipientSmartAccount.selfDeploy(true);
}

console.log("🆕 depositRecipient Smart Account Generated:", depositRecipientSmartAccountAddress);

const nil_refund_wallet = ethers.Wallet.createRandom();
const nil_refund_privateKey = nil_refund_wallet.privateKey; // This is a 0x... string

const nil_refund_signer = new LocalECDSAKeySigner({ privateKey: nil_refund_privateKey as Hex });
const feeRefundSmartAccount = new SmartAccountV1({
signer: nil_refund_signer,
client,
salt: BigInt(Math.floor(Math.random() * 10000)),
shardId: 1,
pubkey: nil_refund_signer.getPublicKey(),
});
const feeRefundSmartAccountAddress = feeRefundSmartAccount.address;
await topupSmartAccount(faucetClient, client, feeRefundSmartAccountAddress);

if ((await feeRefundSmartAccount.checkDeploymentStatus()) === false) {
await feeRefundSmartAccount.selfDeploy(true);
}

console.log("🆕 feeRefund Smart Account Generated:", feeRefundSmartAccountAddress);

return {
ownerSmartAccount,
depositRecipientSmartAccount,
feeRefundSmartAccount
};
}

export async function topupSmartAccount(faucetClient: FaucetClient, client: PublicClient, smartAccountAddress: String) {
const topUpFaucet = await faucetClient.topUp({
smartAccountAddress: smartAccountAddress as Hex,
amount: convertEthToWei(0.1),
faucetAddress: process.env.NIL as `0x${string}`,
});
await waitTillCompleted(client, topUpFaucet);
}

87 changes: 87 additions & 0 deletions rollup-bridge-contracts/test/hardhat/bridge_eth_test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { expect } from "chai";
import "@nomicfoundation/hardhat-ethers";
import {
ProcessedReceipt,
} from "@nilfoundation/niljs";
import "dotenv/config";
import type { Abi } from "abitype";
import { encodeFunctionData } from "viem";
import { generateL2RelayMessage } from "./generate-l2-relay-message";
import { bigIntReplacer } from "../../deploy/config/config-helper";
import { L2BridgeTestFixtureResult, setupL2BridgeTestFixture } from "./l2-bridge-test-fixture";

const l1EthBridgeAddress = '0x0001e0d8f4De4E838a66963f406Fa826cCaCA322';

describe("L2BridgeMessenger Contract", () => {
it("Should accept the (ETHDeposit) message relayed by relayer", async () => {

const l2BridgeTestFixture: L2BridgeTestFixtureResult = await setupL2BridgeTestFixture();

// Relay DepositMessage to L2
const depositorAddressValue = "0xc8d5559BA22d11B0845215a781ff4bF3CCa0EF89";
const depositAmountValue = "1000000000000";
const l2DepositRecipientValue = l2BridgeTestFixture.depositRecipientSmartAccount.address;
const l2FeeRefundAddressValue = l2BridgeTestFixture.feeRefundSmartAccount.address;

try {
const messageSender = l1EthBridgeAddress;
const messageTarget = l2BridgeTestFixture.l2ETHBridgeProxyAddress;
const messageType = "1";
const messageCreatedAt = Math.floor(Date.now() / 1000);
const messageExpiryTime = Math.floor(Date.now() / 1000) + 10000;
const ethDepositRelayMessage = generateL2RelayMessage(depositorAddressValue, depositAmountValue, l2DepositRecipientValue, l2FeeRefundAddressValue);
const nilGasLimit = "1000000";
const maxFeePerGas = "27500000";
const maxPriorityFeePerGas = "1250000";
const feeCredit = "27500000000000";
const messageNonce = 0;

// Dynamically load artifacts
const L2BridgeMessengerJson = await import("../../artifacts/contracts/bridge/l2/L2BridgeMessenger.sol/L2BridgeMessenger.json");

if (!L2BridgeMessengerJson || !L2BridgeMessengerJson.default || !L2BridgeMessengerJson.default.abi || !L2BridgeMessengerJson.default.bytecode) {
throw Error(`Invalid L2BridgeMessengerJson ABI`);
}

const relayMessage = encodeFunctionData({
abi: L2BridgeMessengerJson.default.abi as Abi,
functionName: "relayMessage",
args: [messageSender, messageTarget, messageType, messageNonce, ethDepositRelayMessage, messageExpiryTime],
});

console.log(`generated message to relay on L2BridgeMessenger: ${relayMessage}`);

const relayEthDepositMessageResponse = await l2BridgeTestFixture.ownerSmartAccount.sendTransaction({
to: l2BridgeTestFixture.l2BridgeMessengerProxyAddress as `0x${string}`,
data: relayMessage,
feeCredit: BigInt(feeCredit),
maxFeePerGas: BigInt(maxFeePerGas),
maxPriorityFeePerGas: BigInt(maxPriorityFeePerGas)
});

console.log(`relayMessage transaction was done and awaiting for ProcessedReceipt`);

const relayEthDepositMessageTxnReceipts: ProcessedReceipt[] = await relayEthDepositMessageResponse.wait();

const relayedEthDepositMessageTxnReceipt: ProcessedReceipt = relayEthDepositMessageTxnReceipts[0] as ProcessedReceipt;

const outputReceipts: ProcessedReceipt[] = relayedEthDepositMessageTxnReceipt.outputReceipts as ProcessedReceipt[];

console.log(`outputReceipt is: ${JSON.stringify(outputReceipts, bigIntReplacer, 2)}`)

const outputReceipt: ProcessedReceipt = outputReceipts[0] as ProcessedReceipt;

console.log(`outputReceipt extracted as: ${JSON.stringify(outputReceipt, bigIntReplacer, 2)}`);

// check the first element in the ProcessedReceipt and verify if it is successful
if (!outputReceipt.success) {
console.error(`Failed to relay message
on the L2BridgeMessenger contract: ${l2BridgeTestFixture.l2BridgeMessengerProxyAddress}`);
} else {
console.log(`successfully relayed EthDepositMessage on to L2BridgeMessenger with transactionReceipt: ${JSON.stringify(relayEthDepositMessageTxnReceipts[0], bigIntReplacer, 2)}`);
}
} catch (err) {
console.error(`Failed to relay ethBridge message on to L2BridgeMessenger: ${JSON.stringify(err)}`);
}
});
});
29 changes: 29 additions & 0 deletions rollup-bridge-contracts/test/hardhat/generate-l2-relay-message.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { ethers } from "ethers";

// npx ts-node test/hardhat/generate-l2-relay-message.ts
export const generateL2RelayMessage = (depositorAddress: String, depositAmount: String, l2DepositRecipient: String, l2FeeRefundAddress: String): String => {
const abi = [
"function finaliseETHDeposit(address depositorAddress, uint256 depositAmount, address l2DepositRecipient, address l2FeeRefundAddress)"
];

const iface = new ethers.Interface(abi);

console.log(`about to generate depositMessage Data`);

const depositMessage = iface.encodeFunctionData(
"finaliseETHDeposit",
[depositorAddress, depositAmount, l2DepositRecipient, l2FeeRefundAddress]
);

console.log(depositMessage);

return depositMessage;
}


// const depositorAddressValue = "0xc8d5559BA22d11B0845215a781ff4bF3CCa0EF89";
// const depositAmountValue = "1000000000000";
// const l2DepositRecipientValue = "0x0001D3A5b915Bc99542a9430423cDe75Bd7F7aC7";
// const l2FeeRefundAddressValue = "0x000131b12EBeb7A34e1a47d402137df6De7b08Ae";

// generateL2RelayMessage(depositorAddressValue, depositAmountValue, l2DepositRecipientValue, l2FeeRefundAddressValue);
Loading
Loading