Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: prototype localnet #202

Merged
merged 62 commits into from
Jul 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
1f2625e
initialize gateway
lumtis Jun 18, 2024
bd71007
add receiver example
lumtis Jun 18, 2024
3553a8f
add test examples
lumtis Jun 18, 2024
791834b
add entry for test prototype
lumtis Jun 18, 2024
c1bd47d
typechain
lumtis Jun 18, 2024
6aed43c
change B to non-payable
lumtis Jun 18, 2024
8a973c1
generate
lumtis Jun 18, 2024
d562400
initialize custody contract
lumtis Jun 19, 2024
555ff9d
add execute with erc20 in gateway
lumtis Jun 19, 2024
21b8ace
add test
lumtis Jun 19, 2024
484096a
add some more tests
lumtis Jun 19, 2024
a0c8e5a
initalize uniswap test
lumtis Jun 19, 2024
5b893d2
finish test for uniswap
lumtis Jun 19, 2024
776caef
generate
lumtis Jun 19, 2024
704382b
use custom error
lumtis Jun 19, 2024
5e4766c
add some more comment
lumtis Jun 19, 2024
ce87491
a bit more comments
lumtis Jun 19, 2024
049f956
Merge branch 'main' into init-prototypes
skosito Jun 20, 2024
278c327
chore: change Gateway contract to be upgradable (#175)
skosito Jun 26, 2024
9303311
conflicts
lumtis Jun 27, 2024
cb0ca6c
fix yarn.lock
lumtis Jun 27, 2024
b9e5bfd
feat: inbound evm prototype (#178)
skosito Jun 27, 2024
9a785bc
feat: prototype inbound zevm (#183)
skosito Jul 1, 2024
430992a
feat: prototype outbound zevm (#191)
skosito Jul 1, 2024
a09cc0c
start worker script to deploy evm and zevm contracts on local hardhat…
skosito Jul 1, 2024
b39bc57
add simple script to deposit to gateway evm and listen to event in wo…
skosito Jul 1, 2024
4ee9023
Merge branch 'main' into init-prototypes
skosito Jul 1, 2024
d784b8e
Merge branch 'init-prototypes' into prototype-localnet
skosito Jul 1, 2024
7e3ad3e
fix coderabbit comments
skosito Jul 2, 2024
0b1f894
fix coderabbit comments
skosito Jul 2, 2024
7db93dd
fix yarn generate
skosito Jul 2, 2024
69d5763
Merge branch 'main' into init-prototypes
skosito Jul 2, 2024
022fee2
Merge branch 'init-prototypes' into prototype-localnet
skosito Jul 2, 2024
1e8af88
generate
skosito Jul 2, 2024
d94d8ff
renamings
skosito Jul 2, 2024
290a84e
add simple task to call receiver
skosito Jul 2, 2024
5817d9a
use receiver from call event in worker script
skosito Jul 2, 2024
f7d18fd
add zcontract localnet task
skosito Jul 2, 2024
51ff6a2
cleanup
skosito Jul 2, 2024
b857b9c
rename localnet script to worker
skosito Jul 2, 2024
cbd7dcf
run localnode and worker concurrently
skosito Jul 3, 2024
da9c0cd
different colors and prefix for node and worker
skosito Jul 3, 2024
6bd8c16
Merge branch 'main' into prototype-localnet
skosito Jul 3, 2024
f2a3371
fixes after merge
skosito Jul 3, 2024
cc93e3e
fixes after merge
skosito Jul 3, 2024
5a5f76b
fix tests
skosito Jul 3, 2024
941949e
fix worker after merge
skosito Jul 3, 2024
6275282
add more tasks
skosito Jul 3, 2024
8c33743
wait on hardhat node before starting worker
skosito Jul 3, 2024
fa96e30
remove redundant hardhat config
skosito Jul 3, 2024
b5a665a
generate
skosito Jul 3, 2024
badd4f8
add tasks to coderabbit
skosito Jul 3, 2024
d158c71
improve instructions
skosito Jul 3, 2024
49223b2
exclude in coderabbit
skosito Jul 3, 2024
4275805
test config changes
skosito Jul 3, 2024
caae687
revert
skosito Jul 3, 2024
0c1b370
add try catch
skosito Jul 3, 2024
76f284d
Merge branch 'main' into prototype-localnet
skosito Jul 3, 2024
e62b051
cleanup
skosito Jul 3, 2024
bb27a54
PR comments
skosito Jul 4, 2024
d80596e
yarn generate
skosito Jul 4, 2024
be1f176
and info on how to hide node logs to readme
skosito Jul 4, 2024
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 @@ -4,7 +4,7 @@ pragma solidity 0.8.7;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

contract Receiver {
contract ReceiverEVM {
using SafeERC20 for IERC20;
skosito marked this conversation as resolved.
Show resolved Hide resolved

event ReceivedPayable(address sender, uint256 value, string str, uint256 num, bool flag);
Expand Down
2 changes: 1 addition & 1 deletion contracts/prototypes/zevm/GatewayZEVM.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ contract GatewayZEVM is Initializable, OwnableUpgradeable, UUPSUpgradeable {
error CallerIsNotFungibleModule();
error InvalidTarget();

event Call(address indexed sender, bytes indexed receiver, bytes message);
event Call(address indexed sender, bytes receiver, bytes message);
skosito marked this conversation as resolved.
Show resolved Hide resolved
event Withdrawal(address indexed from, bytes to, uint256 value, uint256 gasfee, uint256 protocolFlatFee, bytes message);

/// @custom:oz-upgrades-unsafe-allow constructor
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "./interfaces.sol";
import "../../zevm/interfaces/IZRC20.sol";

contract Sender {
contract SenderZEVM {
skosito marked this conversation as resolved.
Show resolved Hide resolved
address public gateway;
skosito marked this conversation as resolved.
Show resolved Hide resolved
error ApprovalFailed();

Expand Down
1 change: 1 addition & 0 deletions hardhat.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import "uniswap-v2-deploy-plugin";
import "solidity-coverage";
import "hardhat-gas-reporter";
import "./tasks/addresses";
import "./tasks/localnet";
import "@openzeppelin/hardhat-upgrades";

import { getHardhatConfigNetworks } from "@zetachain/networks";
Expand Down
8 changes: 6 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"@zetachain/networks": "^8.0.0",
"axios": "^1.6.5",
"chai": "^4.3.6",
"concurrently": "^8.2.2",
"cpx": "^1.5.0",
"del-cli": "^5.0.0",
"dotenv": "^16.0.0",
Expand Down Expand Up @@ -58,7 +59,8 @@
"tsconfig-paths": "^3.14.1",
"typechain": "^8.1.0",
"typescript": "^4.6.3",
"uniswap-v2-deploy-plugin": "^0.0.4"
"uniswap-v2-deploy-plugin": "^0.0.4",
"wait-on": "^7.2.0"
},
"files": [
"contracts",
Expand All @@ -83,10 +85,12 @@
"lint": "npx eslint . --ext .js,.ts",
"lint:fix": "npx eslint . --ext .js,.ts,.json --fix --ignore-pattern coverage/ --ignore-pattern coverage.json",
"lint:sol": "solhint 'contracts/**/*.sol'",
"localnet": "concurrently --names \"NODE,WORKER\" --prefix-colors \"blue.bold,green.bold\" \"npx hardhat node\" \"wait-on tcp:8545 && yarn worker\"",
"prepublishOnly": "yarn build",
"test": "npx hardhat test",
"test:prototypes": "yarn compile && npx hardhat test test/prototypes/*",
"tsc:watch": "npx tsc --watch"
"tsc:watch": "npx tsc --watch",
"worker": "npx hardhat run scripts/worker.ts --network localhost"
},
"types": "./dist/lib/index.d.ts",
"version": "0.0.8"
Expand Down

Large diffs are not rendered by default.

28 changes: 10 additions & 18 deletions pkg/contracts/prototypes/zevm/gatewayzevm.sol/gatewayzevm.go

Large diffs are not rendered by default.

Large diffs are not rendered by default.

15 changes: 15 additions & 0 deletions scripts/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
## Worker script

To start localnet execute:

```
yarn localnet
```

This will run hardhat local node and worker script, which will deploy all contracts, and listen and react to events, facilitating communication between contracts.
Tasks to interact with localnet are located in `tasks/localnet`. To make use of default contract addresses on localnet, start localnet from scratch, so contracts are deployed on same addresses. Otherwise, provide custom addresses as tasks parameters.

To only show logs from worker and hide hardhat node logs:
```
yarn localnet --hide="NODE"
```
202 changes: 202 additions & 0 deletions scripts/worker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
import { AddressZero } from "@ethersproject/constants";
import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers";
import { SystemContract, ZRC20 } from "@typechain-types";
import { parseEther } from "ethers/lib/utils";
import { ethers, upgrades } from "hardhat";

const hre = require("hardhat");

export const FUNGIBLE_MODULE_ADDRESS = "0x735b14BB79463307AAcBED86DAf3322B1e6226aB";

const deploySystemContracts = async (tss: SignerWithAddress) => {
// Prepare EVM
// Deploy system contracts (gateway and custody)
const GatewayEVM = await ethers.getContractFactory("GatewayEVM");
const Custody = await ethers.getContractFactory("ERC20CustodyNew");

const gatewayEVM = await upgrades.deployProxy(GatewayEVM, [tss.address], {
initializer: "initialize",
kind: "uups",
});
console.log("GatewayEVM:", gatewayEVM.address);

const custody = await Custody.deploy(gatewayEVM.address);
await gatewayEVM.setCustody(custody.address);

// Prepare ZEVM
// Deploy system contracts (gateway and system)
const SystemContractFactory = await ethers.getContractFactory("SystemContractMock");
const systemContract = (await SystemContractFactory.deploy(AddressZero, AddressZero, AddressZero)) as SystemContract;

const GatewayZEVM = await ethers.getContractFactory("GatewayZEVM");
const gatewayZEVM = await upgrades.deployProxy(GatewayZEVM, [], {
initializer: "initialize",
kind: "uups",
});
console.log("GatewayZEVM:", gatewayZEVM.address);

return {
custody,
gatewayEVM,
gatewayZEVM,
systemContract,
};
};

const deployTestContracts = async (
systemContracts,
ownerEVM: SignerWithAddress,
ownerZEVM: SignerWithAddress,
fungibleModuleSigner: SignerWithAddress
) => {
// Prepare EVM
// Deploy test contracts (erc20, receiver) and mint funds to test accounts
const TestERC20 = await ethers.getContractFactory("TestERC20");
const ReceiverEVM = await ethers.getContractFactory("ReceiverEVM");

const token = await TestERC20.deploy("Test Token", "TTK");
const receiverEVM = await ReceiverEVM.deploy();
await token.mint(ownerEVM.address, ethers.utils.parseEther("1000"));

// Transfer some tokens to the custody contract
await token.transfer(systemContracts.custody.address, ethers.utils.parseEther("500"));

// Prepare ZEVM
// Deploy test contracts (test zContract, zrc20, sender) and mint funds to test accounts
const TestZContract = await ethers.getContractFactory("TestZContract");
const testZContract = await TestZContract.deploy();

const ZRC20Factory = await ethers.getContractFactory("ZRC20New");
const ZRC20Contract = (await ZRC20Factory.connect(fungibleModuleSigner).deploy(
"TOKEN",
"TKN",
18,
1,
1,
0,
systemContracts.systemContract.address,
systemContracts.gatewayZEVM.address
)) as ZRC20;

await systemContracts.systemContract.setGasCoinZRC20(1, ZRC20Contract.address);
await systemContracts.systemContract.setGasPrice(1, ZRC20Contract.address);
await ZRC20Contract.connect(fungibleModuleSigner).deposit(ownerZEVM.address, parseEther("100"));
await ZRC20Contract.connect(ownerZEVM).approve(systemContracts.gatewayZEVM.address, parseEther("100"));

// Include abi of gatewayZEVM events, so hardhat can decode them automatically
const senderArtifact = await hre.artifacts.readArtifact("SenderZEVM");
const gatewayZEVMArtifact = await hre.artifacts.readArtifact("GatewayZEVM");
const senderABI = [
...senderArtifact.abi,
...gatewayZEVMArtifact.abi.filter((f: ethers.utils.Fragment) => f.type === "event"),
];

const SenderZEVM = new ethers.ContractFactory(senderABI, senderArtifact.bytecode, ownerZEVM);
const senderZEVM = await SenderZEVM.deploy(systemContracts.gatewayZEVM.address);
await ZRC20Contract.connect(fungibleModuleSigner).deposit(senderZEVM.address, parseEther("100"));

return {
ZRC20Contract,
receiverEVM,
senderZEVM,
testZContract,
};
};

export const startWorker = async () => {
const [ownerEVM, ownerZEVM, tss] = await ethers.getSigners();

// Impersonate the fungible module account
await hre.network.provider.request({
method: "hardhat_impersonateAccount",
params: [FUNGIBLE_MODULE_ADDRESS],
});

// Get a signer for the fungible module account
const fungibleModuleSigner = await ethers.getSigner(FUNGIBLE_MODULE_ADDRESS);
hre.network.provider.send("hardhat_setBalance", [FUNGIBLE_MODULE_ADDRESS, parseEther("1000000").toHexString()]);

// Deploy system and test contracts
const systemContracts = await deploySystemContracts(tss);
const testContracts = await deployTestContracts(systemContracts, ownerEVM, ownerZEVM, fungibleModuleSigner);

// Listen to contracts events
// event Call(address indexed sender, bytes receiver, bytes message);
systemContracts.gatewayZEVM.on("Call", async (...args: Array<any>) => {
console.log("Worker: Call event on GatewayZEVM.");
console.log("Worker: Calling ReceiverEVM through GatewayEVM...");
const receiver = args[1];
const message = args[2];
const executeTx = await systemContracts.gatewayEVM.execute(receiver, message, { value: 0 });
await executeTx.wait();
});

// event Withdrawal(address indexed from, bytes to, uint256 value, uint256 gasfee, uint256 protocolFlatFee, bytes message);
systemContracts.gatewayZEVM.on("Withdrawal", async (...args: Array<any>) => {
console.log("Worker: Withdrawal event on GatewayZEVM.");
const receiver = args[1];
const message = args[5];
if (message != "0x") {
console.log("Worker: Calling ReceiverEVM through GatewayEVM...");
const executeTx = await systemContracts.gatewayEVM.execute(receiver, message, { value: 0 });
await executeTx.wait();
}
});

testContracts.receiverEVM.on("ReceivedPayable", () => {
console.log("ReceiverEVM: receivePayable called!");
});

// event Call(address indexed sender, address indexed receiver, bytes payload);
systemContracts.gatewayEVM.on("Call", async (...args: Array<any>) => {
console.log("Worker: Call event on GatewayEVM.");
console.log("Worker: Calling TestZContract through GatewayZEVM...");
const zContract = args[1];
const payload = args[2];
const executeTx = await systemContracts.gatewayZEVM.connect(fungibleModuleSigner).execute(
[systemContracts.gatewayZEVM.address, fungibleModuleSigner.address, 1],
// onCrosschainCall contains zrc20 and amount which is not available in Call event
testContracts.ZRC20Contract.address,
parseEther("0"),
zContract,
payload
);
await executeTx.wait();
});

// event Deposit(address indexed sender, address indexed receiver, uint256 amount, address asset, bytes payload);
systemContracts.gatewayEVM.on("Deposit", async (...args: Array<any>) => {
console.log("Worker: Deposit event on GatewayEVM.");
const receiver = args[1];
const asset = args[3];
const payload = args[4];
if (payload != "0x") {
console.log("Worker: Calling TestZContract through GatewayZEVM...");
const executeTx = await systemContracts.gatewayZEVM
.connect(fungibleModuleSigner)
.execute(
[systemContracts.gatewayZEVM.address, fungibleModuleSigner.address, 1],
asset,
parseEther("0"),
receiver,
payload
);
await executeTx.wait();
}
});

testContracts.testZContract.on("ContextData", async () => {
console.log("TestZContract: onCrosschainCall called!");
});

process.stdin.resume();
};

startWorker()
.then(() => {
console.log("Setup complete, monitoring events. Press CTRL+C to exit.");
})
.catch((error) => {
console.error("Failed to deploy contracts or set up listeners:", error);
process.exit(1);
});
104 changes: 104 additions & 0 deletions tasks/localnet.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import { task } from "hardhat/config";

declare const hre: any;

// Contains tasks to make it easier to interact with prototype contracts localnet.
// To make use of default contract addresses on localnet, start localnet from scratch, so contracts are deployed on same addresses.
// Otherwise, provide custom addresses as parameters.

task("zevm-call", "calls evm contract from zevm account")
.addOptionalParam("gatewayZEVM", "contract address of gateway on ZEVM", "0x413b1AfCa96a3df5A686d8BFBF93d30688a7f7D9")
.addOptionalParam("receiverEVM", "contract address of receiver on EVM", "0x821f3361D454cc98b7555221A06Be563a7E2E0A6")
.setAction(async (taskArgs) => {
const gatewayZEVM = await hre.ethers.getContractAt("GatewayZEVM", taskArgs.gatewayZEVM);
const receiverEVM = await hre.ethers.getContractAt("ReceiverEVM", taskArgs.receiverEVM);

const str = "Hello!";
const num = 42;
const flag = true;

// Encode the function call data and call on zevm
const message = receiverEVM.interface.encodeFunctionData("receivePayable", [str, num, flag]);
try {
const callTx = await gatewayZEVM.call(receiverEVM.address, message);
await callTx.wait();
console.log("ReceiverEVM called from ZEVM");
} catch (e) {
console.error("Error calling ReceiverEVM:", e);
}
});

task("zevm-withdraw-and-call", "withdraws zrc20 and calls evm contract from zevm account")
.addOptionalParam("gatewayZEVM", "contract address of gateway on ZEVM", "0x413b1AfCa96a3df5A686d8BFBF93d30688a7f7D9")
.addOptionalParam("receiverEVM", "contract address of receiver on EVM", "0x821f3361D454cc98b7555221A06Be563a7E2E0A6")
.addOptionalParam("zrc20", "contract address of zrc20", "0x9fd96203f7b22bCF72d9DCb40ff98302376cE09c")
.addOptionalParam("amount", "amount to withdraw", "1")
.setAction(async (taskArgs) => {
const gatewayZEVM = await hre.ethers.getContractAt("GatewayZEVM", taskArgs.gatewayZEVM);
const receiverEVM = await hre.ethers.getContractAt("ReceiverEVM", taskArgs.receiverEVM);
const zrc20 = await hre.ethers.getContractAt("ZRC20New", taskArgs.zrc20);
const [, ownerZEVM] = await hre.ethers.getSigners();

const str = "Hello!";
const num = 42;
const flag = true;

// Encode the function call data and call on zevm
const message = receiverEVM.interface.encodeFunctionData("receivePayable", [str, num, flag]);

try {
const callTx = await gatewayZEVM
.connect(ownerZEVM)
.withdrawAndCall(receiverEVM.address, hre.ethers.utils.parseEther(taskArgs.amount), zrc20.address, message);
await callTx.wait();
console.log("ReceiverEVM called from ZEVM");
} catch (e) {
console.error("Error calling ReciverEVM:", e);
}
});

task("evm-call", "calls zevm zcontract from evm account")
.addOptionalParam("gatewayEVM", "contract address of gateway on EVM", "0xB06c856C8eaBd1d8321b687E188204C1018BC4E5")
.addOptionalParam("zContract", "contract address of zContract on ZEVM", "0x71089Ba41e478702e1904692385Be3972B2cBf9e")
.setAction(async (taskArgs) => {
const gatewayEVM = await hre.ethers.getContractAt("GatewayEVM", taskArgs.gatewayEVM);
const zContract = await hre.ethers.getContractAt("TestZContract", taskArgs.zContract);

const message = hre.ethers.utils.defaultAbiCoder.encode(["string"], ["hello"]);

try {
const callTx = await gatewayEVM.call(zContract.address, message);
await callTx.wait();
console.log("TestZContract called from EVM");
} catch (e) {
console.error("Error calling TestZContract:", e);
}
});

task("evm-deposit-and-call", "deposits erc20 and calls zevm zcontract from evm account")
.addOptionalParam("gatewayEVM", "contract address of gateway on EVM", "0xB06c856C8eaBd1d8321b687E188204C1018BC4E5")
.addOptionalParam("zContract", "contract address of zContract on ZEVM", "0x71089Ba41e478702e1904692385Be3972B2cBf9e")
.addOptionalParam("erc20", "contract address of erc20", "0x02df3a3F960393F5B349E40A599FEda91a7cc1A7")
.addOptionalParam("amount", "amount to deposit", "1")
.setAction(async (taskArgs) => {
const gatewayEVM = await hre.ethers.getContractAt("GatewayEVM", taskArgs.gatewayEVM);
const zContract = await hre.ethers.getContractAt("TestZContract", taskArgs.zContract);
const erc20 = await hre.ethers.getContractAt("TestERC20", taskArgs.erc20);

await erc20.approve(gatewayEVM.address, hre.ethers.utils.parseEther(taskArgs.amount));

const payload = hre.ethers.utils.defaultAbiCoder.encode(["string"], ["hello"]);

try {
const callTx = await gatewayEVM["depositAndCall(address,uint256,address,bytes)"](
zContract.address,
hre.ethers.utils.parseEther(taskArgs.amount),
erc20.address,
payload
);
await callTx.wait();
console.log("TestZContract called from EVM");
} catch (e) {
console.error("Error calling TestZContract:", e);
}
});
Loading
Loading