From d2092bcdc194d2b2ee8b5141ed787881f75fbc44 Mon Sep 17 00:00:00 2001 From: Alain Olivier Date: Thu, 2 Nov 2023 14:03:24 +0100 Subject: [PATCH 01/19] feat(evm): add adapter for pNetwork v2 --- .../adapters/pnetwork/IErc20Vault.sol | 13 + .../contracts/adapters/pnetwork/IPToken.sol | 12 + .../adapters/pnetwork/PNetworkAdapter.sol | 46 +++ .../pnetwork/PNetworkMessageRelayer.sol | 122 ++++++++ .../adapters/pnetwork/test/ERC777Token.sol | 25 ++ .../adapters/pnetwork/test/MockVault.sol | 143 +++++++++ .../adapters/pnetwork/test/PToken.sol | 47 +++ .../pnetwork/01_PNetworkMessageRelay.spec.ts | 190 ++++++++++++ .../pnetwork/02_PNetworkAdapter.spec.ts | 272 ++++++++++++++++++ .../evm/test/adapters/pnetwork/utils.spec.ts | 24 ++ 10 files changed, 894 insertions(+) create mode 100644 packages/evm/contracts/adapters/pnetwork/IErc20Vault.sol create mode 100644 packages/evm/contracts/adapters/pnetwork/IPToken.sol create mode 100644 packages/evm/contracts/adapters/pnetwork/PNetworkAdapter.sol create mode 100644 packages/evm/contracts/adapters/pnetwork/PNetworkMessageRelayer.sol create mode 100644 packages/evm/contracts/adapters/pnetwork/test/ERC777Token.sol create mode 100644 packages/evm/contracts/adapters/pnetwork/test/MockVault.sol create mode 100644 packages/evm/contracts/adapters/pnetwork/test/PToken.sol create mode 100644 packages/evm/test/adapters/pnetwork/01_PNetworkMessageRelay.spec.ts create mode 100644 packages/evm/test/adapters/pnetwork/02_PNetworkAdapter.spec.ts create mode 100644 packages/evm/test/adapters/pnetwork/utils.spec.ts diff --git a/packages/evm/contracts/adapters/pnetwork/IErc20Vault.sol b/packages/evm/contracts/adapters/pnetwork/IErc20Vault.sol new file mode 100644 index 00000000..87f6e055 --- /dev/null +++ b/packages/evm/contracts/adapters/pnetwork/IErc20Vault.sol @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.17; + +interface IErc20Vault { + function pegIn( + uint256 tokenAmount, + address tokenAddress, + string memory destinationAddress, + bytes memory userData, + bytes4 destinationChainId + ) external returns (bool); +} diff --git a/packages/evm/contracts/adapters/pnetwork/IPToken.sol b/packages/evm/contracts/adapters/pnetwork/IPToken.sol new file mode 100644 index 00000000..b507a939 --- /dev/null +++ b/packages/evm/contracts/adapters/pnetwork/IPToken.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.17; + +interface IPToken { + function redeem( + uint256 amount, + bytes memory userData, + string memory underlyingAssetRecipient, + bytes4 destinationChainId + ) external; +} diff --git a/packages/evm/contracts/adapters/pnetwork/PNetworkAdapter.sol b/packages/evm/contracts/adapters/pnetwork/PNetworkAdapter.sol new file mode 100644 index 00000000..73a06cfb --- /dev/null +++ b/packages/evm/contracts/adapters/pnetwork/PNetworkAdapter.sol @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: LGPL-3.0-only +pragma solidity ^0.8.17; + +import { OracleAdapter } from "../OracleAdapter.sol"; +import { BlockHashOracleAdapter } from "../BlockHashOracleAdapter.sol"; + +import "@openzeppelin/contracts/token/ERC777/presets/ERC777PresetFixedSupply.sol"; +import "@openzeppelin/contracts-upgradeable/interfaces/IERC1820RegistryUpgradeable.sol"; + +contract PNetworkAdapter is OracleAdapter, BlockHashOracleAdapter, IERC777Recipient { + address public admittedSender; + address public token; + bytes32 public chainId; + IERC1820RegistryUpgradeable private constant _erc1820 = + IERC1820RegistryUpgradeable(0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24); + bytes32 private constant TOKENS_RECIPIENT_INTERFACE_HASH = keccak256("ERC777TokensRecipient"); + bytes32 private constant Erc777Token_INTERFACE_HASH = keccak256("ERC777Token"); + + error ArrayLengthMismatch(); + error InvalidSender(address sender); + + constructor(address _sender, address _token, bytes32 _chainId) { + admittedSender = _sender; + token = _token; + chainId = _chainId; + _erc1820.setInterfaceImplementer(address(this), TOKENS_RECIPIENT_INTERFACE_HASH, address(this)); + } + + // Implement the ERC777TokensRecipient interface + function tokensReceived( + address operator, + address from, + address to, + uint256 amount, + bytes calldata data, + bytes calldata operatorData + ) external override { + require(msg.sender == address(token), "Only accepted token is allowed"); + if (from != admittedSender) revert InvalidSender(from); + (uint256[] memory messageIds, bytes32[] memory hashes) = abi.decode(data, (uint256[], bytes32[])); + if (messageIds.length != hashes.length) revert ArrayLengthMismatch(); + for (uint256 i = 0; i < messageIds.length; i++) { + _storeHash(uint256(chainId), messageIds[i], hashes[i]); + } + } +} diff --git a/packages/evm/contracts/adapters/pnetwork/PNetworkMessageRelayer.sol b/packages/evm/contracts/adapters/pnetwork/PNetworkMessageRelayer.sol new file mode 100644 index 00000000..e6d7ffcf --- /dev/null +++ b/packages/evm/contracts/adapters/pnetwork/PNetworkMessageRelayer.sol @@ -0,0 +1,122 @@ +// SPDX-License-Identifier: LGPL-3.0-only +pragma solidity ^0.8.17; + +import { IMessageRelay } from "../../interfaces/IMessageRelay.sol"; +import { IErc20Vault } from "./IErc20Vault.sol"; +import { IPToken } from "./IPToken.sol"; +import { Yaho } from "../../Yaho.sol"; +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import "@openzeppelin/contracts-upgradeable/utils/introspection/IERC1820RegistryUpgradeable.sol"; +import "@openzeppelin/contracts/token/ERC777/IERC777.sol"; + +contract PNetworkMessageRelay is IMessageRelay { + bytes32 private constant TOKENS_RECIPIENT_INTERFACE_HASH = keccak256("ERC777TokensRecipient"); + IERC1820RegistryUpgradeable private constant _erc1820 = + IERC1820RegistryUpgradeable(0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24); + + address public immutable token; + address public immutable vault; + Yaho public immutable yaho; + bytes4[] private _supportedNetworkIds; + + uint256 private constant SWAP_AMOUNT = 100; + + event MessageRelayed(address indexed emitter, uint256 indexed messageId); + event NetworkIdAdded(bytes4 networkId); + + error AlreadyExistingNetworkId(bytes4 networkId); + + constructor(address _vault, address _token, Yaho _yaho) { + vault = _vault; + token = _token; + yaho = _yaho; + _erc1820.setInterfaceImplementer(address(this), TOKENS_RECIPIENT_INTERFACE_HASH, address(this)); + } + + // TODO: limit access to this function + function addNetwork(bytes4 networkId) public { + if (_isNetworkSupported(networkId)) revert AlreadyExistingNetworkId(networkId); + _supportedNetworkIds.push(networkId); + emit NetworkIdAdded(networkId); + } + + function relayMessages( + uint256[] memory messageIds, + address pnetworkAdapter + ) public payable returns (bytes32 receipt) { + bytes32[] memory hashes = new bytes32[](messageIds.length); + for (uint256 i = 0; i < messageIds.length; i++) { + uint256 id = messageIds[i]; + hashes[i] = yaho.hashes(id); + emit MessageRelayed(address(this), messageIds[i]); + } + bytes memory userData = abi.encode(messageIds, hashes); + if (vault != address(0)) { + for (uint256 index = 0; index < _supportedNetworkIds.length; index++) { + IERC20(token).approve(vault, SWAP_AMOUNT); + IErc20Vault(vault).pegIn( + SWAP_AMOUNT, + token, + _toAsciiString(pnetworkAdapter), + userData, + _supportedNetworkIds[index] + ); + } + } else { + for (uint256 index = 0; index < _supportedNetworkIds.length; index++) { + IPToken(token).redeem( + SWAP_AMOUNT, + userData, + _toAsciiString(pnetworkAdapter), + _supportedNetworkIds[index] + ); + } + } + // TODO: return something resembling a receipt + } + + /** + * @dev Implementation of IERC777Recipient. + */ + function tokensReceived( + address /*operator*/, + address from, + address to, + uint256 amount, + bytes calldata userData, + bytes calldata /*operatorData*/ + ) external onlySupportedToken(msg.sender) { + require(to == address(this), "Token receiver is not this contract"); + } + + modifier onlySupportedToken(address _tokenAddress) { + require(_tokenAddress == token, "Token at supplied address is NOT supported!"); + _; + } + + function _char(bytes1 b) internal pure returns (bytes1 c) { + if (uint8(b) < 10) return bytes1(uint8(b) + 0x30); + else return bytes1(uint8(b) + 0x57); + } + + function _isNetworkSupported(bytes4 _target) internal view returns (bool) { + for (uint i = 0; i < _supportedNetworkIds.length; i++) { + if (_supportedNetworkIds[i] == _target) { + return true; // Value found in the array + } + } + return false; // Value not found in the array + } + + function _toAsciiString(address x) internal pure returns (string memory) { + bytes memory s = new bytes(40); + for (uint i = 0; i < 20; i++) { + bytes1 b = bytes1(uint8(uint(uint160(x)) / (2 ** (8 * (19 - i))))); + bytes1 hi = bytes1(uint8(b) / 16); + bytes1 lo = bytes1(uint8(b) - 16 * uint8(hi)); + s[2 * i] = _char(hi); + s[2 * i + 1] = _char(lo); + } + return string(s); + } +} diff --git a/packages/evm/contracts/adapters/pnetwork/test/ERC777Token.sol b/packages/evm/contracts/adapters/pnetwork/test/ERC777Token.sol new file mode 100644 index 00000000..08563ba0 --- /dev/null +++ b/packages/evm/contracts/adapters/pnetwork/test/ERC777Token.sol @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "@openzeppelin/contracts/token/ERC777/ERC777.sol"; + +contract ERC777Token is ERC777 { + constructor( + string memory name, + string memory symbol, + address[] memory defaultOperators + ) ERC777(name, symbol, defaultOperators) { + _mint(msg.sender, 1000000, "", ""); + } + + function mint( + address recipient, + uint256 value, + bytes memory userData, + bytes memory operatorData + ) public returns (bool) { + require(recipient != address(this), "Recipient cannot be the token contract address!"); + _mint(recipient, value, userData, operatorData); + return true; + } +} diff --git a/packages/evm/contracts/adapters/pnetwork/test/MockVault.sol b/packages/evm/contracts/adapters/pnetwork/test/MockVault.sol new file mode 100644 index 00000000..0564c291 --- /dev/null +++ b/packages/evm/contracts/adapters/pnetwork/test/MockVault.sol @@ -0,0 +1,143 @@ +// SPDX-License-Identifier: MIT + +// NOTE: This special version of the pTokens-erc20-vault is for ETH mainnet, and includes custom +// logic to handle ETHPNT<->PNT fungibility, as well as custom logic to handle GALA tokens after +// they upgraded from v1 to v2. + +pragma solidity ^0.8.0; + +import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; +import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; +import "@openzeppelin/contracts-upgradeable/token/ERC777/IERC777Upgradeable.sol"; +import "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol"; +import "@openzeppelin/contracts-upgradeable/utils/structs/EnumerableSetUpgradeable.sol"; +import "@openzeppelin/contracts-upgradeable/token/ERC777/IERC777RecipientUpgradeable.sol"; +import "@openzeppelin/contracts-upgradeable/utils/introspection/IERC1820RegistryUpgradeable.sol"; + +contract MockVault is Initializable, IERC777RecipientUpgradeable { + using SafeERC20Upgradeable for IERC20Upgradeable; + using EnumerableSetUpgradeable for EnumerableSetUpgradeable.AddressSet; + + IERC1820RegistryUpgradeable private constant _erc1820 = + IERC1820RegistryUpgradeable(0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24); + bytes32 private constant TOKENS_RECIPIENT_INTERFACE_HASH = keccak256("ERC777TokensRecipient"); + bytes32 private constant Erc777Token_INTERFACE_HASH = keccak256("ERC777Token"); + EnumerableSetUpgradeable.AddressSet private supportedTokens; + bytes4 public ORIGIN_CHAIN_ID; + + event PegIn( + address _tokenAddress, + address _tokenSender, + uint256 _tokenAmount, + string _destinationAddress, + bytes _userData, + bytes4 _originChainId, + bytes4 _destinationChainId + ); + + function initialize(address[] memory _tokensToSupport, bytes4 _originChainId) public initializer { + for (uint256 i = 0; i < _tokensToSupport.length; i++) { + supportedTokens.add(_tokensToSupport[i]); + } + _erc1820.setInterfaceImplementer(address(this), TOKENS_RECIPIENT_INTERFACE_HASH, address(this)); + ORIGIN_CHAIN_ID = _originChainId; + } + + modifier onlySupportedTokens(address _tokenAddress) { + require(supportedTokens.contains(_tokenAddress), "Token at supplied address is NOT supported!"); + _; + } + + function pegIn( + uint256 _tokenAmount, + address _tokenAddress, + string calldata _destinationAddress, + bytes4 _destinationChainId + ) external returns (bool) { + return pegIn(_tokenAmount, _tokenAddress, _destinationAddress, "", _destinationChainId); + } + + function pegIn( + uint256 _tokenAmount, + address _tokenAddress, + string memory _destinationAddress, + bytes memory _userData, + bytes4 _destinationChainId + ) public onlySupportedTokens(_tokenAddress) returns (bool) { + require(_tokenAmount > 0, "Token amount must be greater than zero!"); + IERC20Upgradeable(_tokenAddress).safeTransferFrom(msg.sender, address(this), _tokenAmount); + + require(_tokenAddress != address(0), "`_tokenAddress` is set to zero address!"); + + emit PegIn( + _tokenAddress, + msg.sender, + _tokenAmount, + _destinationAddress, + _userData, + ORIGIN_CHAIN_ID, + _destinationChainId + ); + + return true; + } + + /** + * @dev Implementation of IERC777Recipient. + */ + function tokensReceived( + address /*operator*/, + address from, + address to, + uint256 amount, + bytes calldata userData, + bytes calldata /*operatorData*/ + ) external override onlySupportedTokens(msg.sender) { + require(to == address(this), "Token receiver is not this contract"); + if (userData.length > 0) { + require(amount > 0, "Token amount must be greater than zero!"); + (bytes32 tag, string memory _destinationAddress, bytes4 _destinationChainId) = abi.decode( + userData, + (bytes32, string, bytes4) + ); + require(tag == keccak256("ERC777-pegIn"), "Invalid tag for automatic pegIn on ERC777 send"); + emit PegIn(msg.sender, from, amount, _destinationAddress, userData, ORIGIN_CHAIN_ID, _destinationChainId); + } + } + + function pegOut( + address payable _tokenRecipient, + address _tokenAddress, + uint256 _tokenAmount, + bytes calldata _userData + ) external returns (bool success) { + return pegOutTokens(_tokenAddress, _tokenRecipient, _tokenAmount, _userData); + } + + function pegOutTokens( + address _tokenAddress, + address _tokenRecipient, + uint256 _tokenAmount, + bytes memory _userData + ) internal returns (bool success) { + if (tokenIsErc777(_tokenAddress)) { + // NOTE: This is an ERC777 token, so let's use its `send` function so that hooks are called... + IERC777Upgradeable(_tokenAddress).send(_tokenRecipient, _tokenAmount, _userData); + } else { + // NOTE: Otherwise, we use standard ERC20 transfer function instead. + IERC20Upgradeable(_tokenAddress).safeTransfer(_tokenRecipient, _tokenAmount); + } + return true; + } + + function tokenIsErc777(address _tokenAddress) internal view returns (bool) { + return _erc1820.getInterfaceImplementer(_tokenAddress, Erc777Token_INTERFACE_HASH) != address(0); + } + + receive() external payable {} + + function changeOriginChainId(bytes4 _newOriginChainId) public returns (bool success) { + ORIGIN_CHAIN_ID = _newOriginChainId; + return true; + } +} diff --git a/packages/evm/contracts/adapters/pnetwork/test/PToken.sol b/packages/evm/contracts/adapters/pnetwork/test/PToken.sol new file mode 100644 index 00000000..5b4df604 --- /dev/null +++ b/packages/evm/contracts/adapters/pnetwork/test/PToken.sol @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "@openzeppelin/contracts/token/ERC777/ERC777.sol"; + +contract PToken is ERC777 { + bytes4 public ORIGIN_CHAIN_ID; + + event Redeem( + address indexed redeemer, + uint256 value, + string underlyingAssetRecipient, + bytes userData, + bytes4 originChainId, + bytes4 destinationChainId + ); + + constructor( + string memory name, + string memory symbol, + address[] memory defaultOperators + ) ERC777(name, symbol, defaultOperators) { + ORIGIN_CHAIN_ID = 0x87654321; + _mint(msg.sender, 1000000, "", ""); + } + + function mint( + address recipient, + uint256 value, + bytes memory userData, + bytes memory operatorData + ) public returns (bool) { + require(recipient != address(this), "Recipient cannot be the token contract address!"); + _mint(recipient, value, userData, operatorData); + return true; + } + + function redeem( + uint256 amount, + bytes memory userData, + string memory underlyingAssetRecipient, + bytes4 destinationChainId + ) public { + _burn(_msgSender(), amount, userData, ""); + emit Redeem(msg.sender, amount, underlyingAssetRecipient, userData, ORIGIN_CHAIN_ID, destinationChainId); + } +} diff --git a/packages/evm/test/adapters/pnetwork/01_PNetworkMessageRelay.spec.ts b/packages/evm/test/adapters/pnetwork/01_PNetworkMessageRelay.spec.ts new file mode 100644 index 00000000..8cc6f583 --- /dev/null +++ b/packages/evm/test/adapters/pnetwork/01_PNetworkMessageRelay.spec.ts @@ -0,0 +1,190 @@ +import { expect } from "chai" +import { ethers, network } from "hardhat" + +import { ZERO_ADDRESS, deployErc1820Registry, resetNetwork } from "./utils.spec" + +const DOMAIN_ID = "0x0000000000000000000000000000000000000000000000000000000000007a69" + +describe("PNetworkMessageRelayer", function () { + describe("Native Network", () => { + const setup = async () => { + await resetNetwork() + const [wallet] = await ethers.getSigners() + const Yaho = await ethers.getContractFactory("Yaho") + const yaho = await Yaho.deploy() + await deployErc1820Registry(wallet) + const ERC777Token = await ethers.getContractFactory("ERC777Token") + const erc777Token = await ERC777Token.deploy("ERC777 Token", "E777", []) + const anotherErc777Token = await ERC777Token.deploy("Another ERC777 Token", "A777", []) + const Vault = await ethers.getContractFactory("MockVault") + const vault = await Vault.deploy() + await vault.initialize([erc777Token.address, anotherErc777Token.address], "0x12345678") + const PNetworkMessageRelay = await ethers.getContractFactory("PNetworkMessageRelay") + const pNetworkMessageRelay = await PNetworkMessageRelay.deploy(vault.address, erc777Token.address, yaho.address) + await pNetworkMessageRelay.addNetwork("0x87654321") + await pNetworkMessageRelay.addNetwork("0x11223344") + + await erc777Token.connect(wallet).send(pNetworkMessageRelay.address, 10000, "0x") + + const PNetworkAdapter = await ethers.getContractFactory("PNetworkAdapter") + const pNetworkAdapter = await PNetworkAdapter.deploy(vault.address, pNetworkMessageRelay.address, DOMAIN_ID) + const PingPong = await ethers.getContractFactory("PingPong") + const pingPong = await PingPong.deploy() + const message_1 = { + to: pingPong.address, + toChainId: 1, + data: pingPong.interface.getSighash("ping"), + } + + await yaho.dispatchMessages([message_1, message_1]) + + return { + erc777Token, + anotherErc777Token, + vault, + wallet, + yaho, + pNetworkMessageRelay, + pNetworkAdapter, + message_1, + pingPong, + } + } + + describe("Deploy", function () { + it("Successfully deploys contract", async function () { + const { pNetworkMessageRelay, erc777Token, vault, yaho } = await setup() + expect(await pNetworkMessageRelay.deployed()) + expect(await pNetworkMessageRelay.token()).to.equal(erc777Token.address) + expect(await pNetworkMessageRelay.vault()).to.equal(vault.address) + expect(await pNetworkMessageRelay.yaho()).to.equal(yaho.address) + }) + + it("Should not permit to add again the same network", async function () { + const { pNetworkMessageRelay } = await setup() + await expect(pNetworkMessageRelay.addNetwork("0x11223344")).to.revertedWithCustomError( + pNetworkMessageRelay, + "AlreadyExistingNetworkId", + ) + }) + }) + + describe("relayMessages()", function () { + it("Relays message hashes over pNetwork", async function () { + const { pNetworkMessageRelay, pNetworkAdapter, vault, erc777Token } = await setup() + await expect(pNetworkMessageRelay.relayMessages([0, 1], pNetworkAdapter.address)) + .to.emit(pNetworkMessageRelay, "MessageRelayed") + .withArgs(pNetworkMessageRelay.address, 0) + .and.to.emit(vault, "PegIn") + .withArgs( + erc777Token.address, + pNetworkMessageRelay.address, + 100, + pNetworkAdapter.address.replace("0x", "").toLowerCase(), + "0x000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002fd0a21687761d1abc40ff03c81caca3fed1ee30a97ad6255c84706d3afe9fa2b92b1ce5792dbd6e0d8e27dc9819fee7d061c1836f6d145383057377bc24c9301", + "0x12345678", + "0x87654321", + ) + .and.to.emit(vault, "PegIn") + .withArgs( + erc777Token.address, + pNetworkMessageRelay.address, + 100, + pNetworkAdapter.address.replace("0x", "").toLowerCase(), + "0x000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002fd0a21687761d1abc40ff03c81caca3fed1ee30a97ad6255c84706d3afe9fa2b92b1ce5792dbd6e0d8e27dc9819fee7d061c1836f6d145383057377bc24c9301", + "0x12345678", + "0x11223344", + ) + }) + }) + }) + + describe("Host Network", () => { + const setup = async () => { + await resetNetwork() + const [wallet] = await ethers.getSigners() + const Yaho = await ethers.getContractFactory("Yaho") + const yaho = await Yaho.deploy() + await deployErc1820Registry(wallet) + const PToken = await ethers.getContractFactory("PToken") + const pToken = await PToken.deploy("pToken", "P", []) + const anotherPToken = await PToken.deploy("Another ERC777 Token", "A777", []) + const Vault = await ethers.getContractFactory("MockVault") + const vault = await Vault.deploy() + await vault.initialize([pToken.address, anotherPToken.address], "0x12345678") + const PNetworkMessageRelay = await ethers.getContractFactory("PNetworkMessageRelay") + const pNetworkMessageRelay = await PNetworkMessageRelay.deploy(ZERO_ADDRESS, pToken.address, yaho.address) + await pNetworkMessageRelay.addNetwork("0x12345678") + await pNetworkMessageRelay.addNetwork("0x11223344") + + await pToken.connect(wallet).send(pNetworkMessageRelay.address, 10000, "0x") + + const PNetworkAdapter = await ethers.getContractFactory("PNetworkAdapter") + const pNetworkAdapter = await PNetworkAdapter.deploy(vault.address, pNetworkMessageRelay.address, DOMAIN_ID) + const PingPong = await ethers.getContractFactory("PingPong") + const pingPong = await PingPong.deploy() + const message_1 = { + to: pingPong.address, + toChainId: 1, + data: pingPong.interface.getSighash("ping"), + } + + await yaho.dispatchMessages([message_1, message_1]) + + return { + pToken, + anotherPToken, + vault, + wallet, + yaho, + pNetworkMessageRelay, + pNetworkAdapter, + } + } + + describe("Deploy", function () { + it("Successfully deploys contract", async function () { + const { pNetworkMessageRelay, pToken, yaho } = await setup() + expect(await pNetworkMessageRelay.deployed()) + expect(await pNetworkMessageRelay.token()).to.equal(pToken.address) + expect(await pNetworkMessageRelay.vault()).to.equal(ZERO_ADDRESS) + expect(await pNetworkMessageRelay.yaho()).to.equal(yaho.address) + }) + + it("Should not permit to add again the same network", async function () { + const { pNetworkMessageRelay } = await setup() + await expect(pNetworkMessageRelay.addNetwork("0x11223344")).to.revertedWithCustomError( + pNetworkMessageRelay, + "AlreadyExistingNetworkId", + ) + }) + }) + + describe("relayMessages()", function () { + it("Relays message hashes over pNetwork", async function () { + const { pNetworkMessageRelay, pNetworkAdapter, pToken } = await setup() + await expect(pNetworkMessageRelay.relayMessages([0, 1], pNetworkAdapter.address)) + .to.emit(pNetworkMessageRelay, "MessageRelayed") + .withArgs(pNetworkMessageRelay.address, 0) + .and.to.emit(pToken, "Redeem") + .withArgs( + pNetworkMessageRelay.address, + 100, + pNetworkAdapter.address.replace("0x", "").toLowerCase(), + "0x000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002fd0a21687761d1abc40ff03c81caca3fed1ee30a97ad6255c84706d3afe9fa2b92b1ce5792dbd6e0d8e27dc9819fee7d061c1836f6d145383057377bc24c9301", + "0x87654321", + "0x12345678", + ) + .and.to.emit(pToken, "Redeem") + .withArgs( + pNetworkMessageRelay.address, + 100, + pNetworkAdapter.address.replace("0x", "").toLowerCase(), + "0x000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002fd0a21687761d1abc40ff03c81caca3fed1ee30a97ad6255c84706d3afe9fa2b92b1ce5792dbd6e0d8e27dc9819fee7d061c1836f6d145383057377bc24c9301", + "0x87654321", + "0x11223344", + ) + }) + }) + }) +}) diff --git a/packages/evm/test/adapters/pnetwork/02_PNetworkAdapter.spec.ts b/packages/evm/test/adapters/pnetwork/02_PNetworkAdapter.spec.ts new file mode 100644 index 00000000..ce315331 --- /dev/null +++ b/packages/evm/test/adapters/pnetwork/02_PNetworkAdapter.spec.ts @@ -0,0 +1,272 @@ +import { expect } from "chai" +import { ethers } from "hardhat" + +import { ZERO_ADDRESS, deployErc1820Registry, resetNetwork } from "./utils.spec" + +const DOMAIN_ID = "0x0000000000000000000000000000000000000000000000000000000000007a69" +const ID_ONE = 1 +const ID_TWO = 2 +const HASH_ZERO = "0x0000000000000000000000000000000000000000000000000000000000000000" +const HASH_ONE = "0x0000000000000000000000000000000000000000000000000000000000000001" +const HASH_TWO = "0x0000000000000000000000000000000000000000000000000000000000000002" + +describe("PNetworkAdapter", function () { + describe("Host Blockchain", () => { + const setup = async () => { + await resetNetwork() + const [wallet] = await ethers.getSigners() + await deployErc1820Registry(wallet) + const PToken = await ethers.getContractFactory("PToken") + const pToken = await PToken.deploy("pToken", "P", []) + const PNetworkAdapter = await ethers.getContractFactory("PNetworkAdapter") + const pNetworkAdapter = await PNetworkAdapter.deploy(ZERO_ADDRESS, pToken.address, DOMAIN_ID) + return { + wallet, + pNetworkAdapter, + pToken, + } + } + describe("Constructor", function () { + it("Successfully deploys contract with correct state", async function () { + const { pNetworkAdapter, pToken } = await setup() + expect(await pNetworkAdapter.deployed()) + expect(await pNetworkAdapter.token()).to.equal(pToken.address) + expect(await pNetworkAdapter.admittedSender()).to.equal(ZERO_ADDRESS) + expect(await pNetworkAdapter.chainId()).to.equal(DOMAIN_ID) + }) + }) + + describe("StoreHashes()", function () { + it("Stores hashes", async function () { + const { pNetworkAdapter, wallet, pToken } = await setup() + + const coder = new ethers.utils.AbiCoder() + const data = coder.encode( + ["uint256[]", "bytes32[]"], + [ + [ID_ONE, ID_TWO], + [HASH_ONE, HASH_TWO], + ], + ) + await pToken.connect(wallet).mint(pNetworkAdapter.address, 1000, data, "0x") + expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_ONE)).to.equal(HASH_ONE) + expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_TWO)).to.equal(HASH_TWO) + }) + + it("Should not store hashes when receiving another token", async function () { + const { pNetworkAdapter, wallet } = await setup() + + const PToken = await ethers.getContractFactory("PToken") + const fakePToken = await PToken.deploy("pToken", "P", []) + + const coder = new ethers.utils.AbiCoder() + const data = coder.encode( + ["uint256[]", "bytes32[]"], + [ + [ID_ONE, ID_TWO], + [HASH_ONE, HASH_TWO], + ], + ) + await expect(fakePToken.connect(wallet).mint(pNetworkAdapter.address, 1000, data, "0x")).to.be.revertedWith( + "Only accepted token is allowed", + ) + expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_ONE)).to.equal(HASH_ZERO) + expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_TWO)).to.equal(HASH_ZERO) + }) + + it("Should not store hashes when tokens are not minted", async function () { + const { pNetworkAdapter, wallet, pToken } = await setup() + + const coder = new ethers.utils.AbiCoder() + const data = coder.encode( + ["uint256[]", "bytes32[]"], + [ + [ID_ONE, ID_TWO], + [HASH_ONE, HASH_TWO], + ], + ) + await expect(pToken.connect(wallet).send(pNetworkAdapter.address, 1000, data)) + .to.be.revertedWithCustomError(pNetworkAdapter, "InvalidSender") + .withArgs(wallet.address) + expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_ONE)).to.equal(HASH_ZERO) + expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_TWO)).to.equal(HASH_ZERO) + }) + + it("Should not store hashes when parallel arrays are mismatched", async function () { + const { pNetworkAdapter, wallet, pToken } = await setup() + + const coder = new ethers.utils.AbiCoder() + const data = coder.encode(["uint256[]", "bytes32[]"], [[ID_ONE, ID_TWO], [HASH_ONE]]) + await expect(pToken.connect(wallet).mint(pNetworkAdapter.address, 1000, data, "0x")) + .to.be.revertedWithCustomError(pNetworkAdapter, "ArrayLengthMismatch") + .withArgs() + expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_ONE)).to.equal(HASH_ZERO) + expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_TWO)).to.equal(HASH_ZERO) + }) + + it("Overwrites previous hashes", async function () { + const { pNetworkAdapter, wallet, pToken } = await setup() + const coder = new ethers.utils.AbiCoder() + let data = coder.encode( + ["uint256[]", "bytes32[]"], + [ + [ID_ONE, ID_TWO], + [HASH_ONE, HASH_TWO], + ], + ) + await pToken.connect(wallet).mint(pNetworkAdapter.address, 1000, data, "0x") + expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_ONE)).to.equal(HASH_ONE) + expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_TWO)).to.equal(HASH_TWO) + data = coder.encode( + ["uint256[]", "bytes32[]"], + [ + [ID_TWO, ID_ONE], + [HASH_ONE, HASH_TWO], + ], + ) + await pToken.connect(wallet).mint(pNetworkAdapter.address, 1000, data, "0x") + expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_ONE)).to.equal(HASH_TWO) + expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_TWO)).to.equal(HASH_ONE) + }) + + it("Returns 0 if no header is stored", async function () { + const { pNetworkAdapter } = await setup() + expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_ONE)).to.equal(HASH_ZERO) + }) + }) + }) + + describe("Native Blockchain", () => { + const setup = async () => { + await resetNetwork() + const [wallet] = await ethers.getSigners() + await deployErc1820Registry(wallet) + const ERC777Token = await ethers.getContractFactory("ERC777Token") + const erc777Token = await ERC777Token.deploy("ERC777 Token", "E777", []) + const anotherErc777Token = await ERC777Token.deploy("Another ERC777 Token", "A777", []) + const Vault = await ethers.getContractFactory("MockVault") + const vault = await Vault.deploy() + await vault.initialize([erc777Token.address, anotherErc777Token.address], "0x12345678") + const PNetworkAdapter = await ethers.getContractFactory("PNetworkAdapter") + const pNetworkAdapter = await PNetworkAdapter.deploy(vault.address, erc777Token.address, DOMAIN_ID) + const coder = new ethers.utils.AbiCoder() + const data = coder.encode( + ["bytes32", "string", "bytes4"], + [ethers.utils.keccak256(ethers.utils.toUtf8Bytes("ERC777-pegIn")), "destination-address", "0x87654321"], + ) + await expect(erc777Token.connect(wallet).send(vault.address, 100, data)).to.emit(vault, "PegIn") + await expect(anotherErc777Token.connect(wallet).send(vault.address, 100, data)).to.emit(vault, "PegIn") + return { + wallet, + vault, + pNetworkAdapter, + erc777Token, + anotherErc777Token, + } + } + describe("Constructor", function () { + it("Successfully deploys contract with correct state", async function () { + const { pNetworkAdapter, erc777Token, vault } = await setup() + expect(await pNetworkAdapter.deployed()) + expect(await pNetworkAdapter.token()).to.equal(erc777Token.address) + expect(await pNetworkAdapter.admittedSender()).to.equal(vault.address) + expect(await pNetworkAdapter.chainId()).to.equal(DOMAIN_ID) + }) + }) + + describe("StoreHashes()", function () { + it("Stores hashes", async function () { + const { pNetworkAdapter, wallet, vault, erc777Token } = await setup() + + const coder = new ethers.utils.AbiCoder() + const data = coder.encode( + ["uint256[]", "bytes32[]"], + [ + [ID_ONE, ID_TWO], + [HASH_ONE, HASH_TWO], + ], + ) + await vault.connect(wallet).pegOut(pNetworkAdapter.address, erc777Token.address, 100, data) + expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_ONE)).to.equal(HASH_ONE) + expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_TWO)).to.equal(HASH_TWO) + }) + + it("Should not store hashes when receiving another token", async function () { + const { pNetworkAdapter, wallet, vault, anotherErc777Token } = await setup() + + const coder = new ethers.utils.AbiCoder() + const data = coder.encode( + ["uint256[]", "bytes32[]"], + [ + [ID_ONE, ID_TWO], + [HASH_ONE, HASH_TWO], + ], + ) + await expect( + vault.connect(wallet).pegOut(pNetworkAdapter.address, anotherErc777Token.address, 100, data), + ).to.be.revertedWith("Only accepted token is allowed") + expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_ONE)).to.equal(HASH_ZERO) + expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_TWO)).to.equal(HASH_ZERO) + }) + + it("Should not store hashes when tokens are not sent by the vault", async function () { + const { pNetworkAdapter, wallet, erc777Token } = await setup() + + const coder = new ethers.utils.AbiCoder() + const data = coder.encode( + ["uint256[]", "bytes32[]"], + [ + [ID_ONE, ID_TWO], + [HASH_ONE, HASH_TWO], + ], + ) + await expect(erc777Token.connect(wallet).send(pNetworkAdapter.address, 100, data)) + .to.be.revertedWithCustomError(pNetworkAdapter, "InvalidSender") + .withArgs(wallet.address) + expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_ONE)).to.equal(HASH_ZERO) + expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_TWO)).to.equal(HASH_ZERO) + }) + + it("Should not store hashes when parallel arrays are mismatched", async function () { + const { pNetworkAdapter, wallet, erc777Token, vault } = await setup() + const coder = new ethers.utils.AbiCoder() + const data = coder.encode(["uint256[]", "bytes32[]"], [[ID_ONE, ID_TWO], [HASH_ONE]]) + await expect(vault.connect(wallet).pegOut(pNetworkAdapter.address, erc777Token.address, 100, data)) + .to.be.revertedWithCustomError(pNetworkAdapter, "ArrayLengthMismatch") + .withArgs() + expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_ONE)).to.equal(HASH_ZERO) + expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_TWO)).to.equal(HASH_ZERO) + }) + + it("Overwrites previous hashes", async function () { + const { pNetworkAdapter, wallet, erc777Token, vault } = await setup() + const coder = new ethers.utils.AbiCoder() + let data = coder.encode( + ["uint256[]", "bytes32[]"], + [ + [ID_ONE, ID_TWO], + [HASH_ONE, HASH_TWO], + ], + ) + await vault.connect(wallet).pegOut(pNetworkAdapter.address, erc777Token.address, 50, data) + expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_ONE)).to.equal(HASH_ONE) + expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_TWO)).to.equal(HASH_TWO) + data = coder.encode( + ["uint256[]", "bytes32[]"], + [ + [ID_TWO, ID_ONE], + [HASH_ONE, HASH_TWO], + ], + ) + await vault.connect(wallet).pegOut(pNetworkAdapter.address, erc777Token.address, 50, data) + expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_ONE)).to.equal(HASH_TWO) + expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_TWO)).to.equal(HASH_ONE) + }) + + it("Returns 0 if no header is stored", async function () { + const { pNetworkAdapter } = await setup() + expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_ONE)).to.equal(HASH_ZERO) + }) + }) + }) +}) diff --git a/packages/evm/test/adapters/pnetwork/utils.spec.ts b/packages/evm/test/adapters/pnetwork/utils.spec.ts new file mode 100644 index 00000000..ad1cadfb --- /dev/null +++ b/packages/evm/test/adapters/pnetwork/utils.spec.ts @@ -0,0 +1,24 @@ +import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers" +import { ethers, network } from "hardhat" + +const ERC1820_DEPLOYER = "0xa990077c3205cbDf861e17Fa532eeB069cE9fF96" +const ERC1820_PAYLOAD = + "0xf90a388085174876e800830c35008080b909e5608060405234801561001057600080fd5b506109c5806100206000396000f3fe608060405234801561001057600080fd5b50600436106100a5576000357c010000000000000000000000000000000000000000000000000000000090048063a41e7d5111610078578063a41e7d51146101d4578063aabbb8ca1461020a578063b705676514610236578063f712f3e814610280576100a5565b806329965a1d146100aa5780633d584063146100e25780635df8122f1461012457806365ba36c114610152575b600080fd5b6100e0600480360360608110156100c057600080fd5b50600160a060020a038135811691602081013591604090910135166102b6565b005b610108600480360360208110156100f857600080fd5b5035600160a060020a0316610570565b60408051600160a060020a039092168252519081900360200190f35b6100e06004803603604081101561013a57600080fd5b50600160a060020a03813581169160200135166105bc565b6101c26004803603602081101561016857600080fd5b81019060208101813564010000000081111561018357600080fd5b82018360208201111561019557600080fd5b803590602001918460018302840111640100000000831117156101b757600080fd5b5090925090506106b3565b60408051918252519081900360200190f35b6100e0600480360360408110156101ea57600080fd5b508035600160a060020a03169060200135600160e060020a0319166106ee565b6101086004803603604081101561022057600080fd5b50600160a060020a038135169060200135610778565b61026c6004803603604081101561024c57600080fd5b508035600160a060020a03169060200135600160e060020a0319166107ef565b604080519115158252519081900360200190f35b61026c6004803603604081101561029657600080fd5b508035600160a060020a03169060200135600160e060020a0319166108aa565b6000600160a060020a038416156102cd57836102cf565b335b9050336102db82610570565b600160a060020a031614610339576040805160e560020a62461bcd02815260206004820152600f60248201527f4e6f7420746865206d616e616765720000000000000000000000000000000000604482015290519081900360640190fd5b6103428361092a565b15610397576040805160e560020a62461bcd02815260206004820152601a60248201527f4d757374206e6f7420626520616e204552433136352068617368000000000000604482015290519081900360640190fd5b600160a060020a038216158015906103b85750600160a060020a0382163314155b156104ff5760405160200180807f455243313832305f4143434550545f4d4147494300000000000000000000000081525060140190506040516020818303038152906040528051906020012082600160a060020a031663249cb3fa85846040518363ffffffff167c01000000000000000000000000000000000000000000000000000000000281526004018083815260200182600160a060020a0316600160a060020a031681526020019250505060206040518083038186803b15801561047e57600080fd5b505afa158015610492573d6000803e3d6000fd5b505050506040513d60208110156104a857600080fd5b5051146104ff576040805160e560020a62461bcd02815260206004820181905260248201527f446f6573206e6f7420696d706c656d656e742074686520696e74657266616365604482015290519081900360640190fd5b600160a060020a03818116600081815260208181526040808320888452909152808220805473ffffffffffffffffffffffffffffffffffffffff19169487169485179055518692917f93baa6efbd2244243bfee6ce4cfdd1d04fc4c0e9a786abd3a41313bd352db15391a450505050565b600160a060020a03818116600090815260016020526040812054909116151561059a5750806105b7565b50600160a060020a03808216600090815260016020526040902054165b919050565b336105c683610570565b600160a060020a031614610624576040805160e560020a62461bcd02815260206004820152600f60248201527f4e6f7420746865206d616e616765720000000000000000000000000000000000604482015290519081900360640190fd5b81600160a060020a031681600160a060020a0316146106435780610646565b60005b600160a060020a03838116600081815260016020526040808220805473ffffffffffffffffffffffffffffffffffffffff19169585169590951790945592519184169290917f605c2dbf762e5f7d60a546d42e7205dcb1b011ebc62a61736a57c9089d3a43509190a35050565b600082826040516020018083838082843780830192505050925050506040516020818303038152906040528051906020012090505b92915050565b6106f882826107ef565b610703576000610705565b815b600160a060020a03928316600081815260208181526040808320600160e060020a031996909616808452958252808320805473ffffffffffffffffffffffffffffffffffffffff19169590971694909417909555908152600284528181209281529190925220805460ff19166001179055565b600080600160a060020a038416156107905783610792565b335b905061079d8361092a565b156107c357826107ad82826108aa565b6107b85760006107ba565b815b925050506106e8565b600160a060020a0390811660009081526020818152604080832086845290915290205416905092915050565b6000808061081d857f01ffc9a70000000000000000000000000000000000000000000000000000000061094c565b909250905081158061082d575080155b1561083d576000925050506106e8565b61084f85600160e060020a031961094c565b909250905081158061086057508015155b15610870576000925050506106e8565b61087a858561094c565b909250905060018214801561088f5750806001145b1561089f576001925050506106e8565b506000949350505050565b600160a060020a0382166000908152600260209081526040808320600160e060020a03198516845290915281205460ff1615156108f2576108eb83836107ef565b90506106e8565b50600160a060020a03808316600081815260208181526040808320600160e060020a0319871684529091529020549091161492915050565b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff161590565b6040517f01ffc9a7000000000000000000000000000000000000000000000000000000008082526004820183905260009182919060208160248189617530fa90519096909550935050505056fea165627a7a72305820377f4a2d4301ede9949f163f319021a6e9c687c292a5e2b2c4734c126b524e6c00291ba01820182018201820182018201820182018201820182018201820182018201820a01820182018201820182018201820182018201820182018201820182018201820" + +export const ZERO_ADDRESS = "0x0000000000000000000000000000000000000000" + +export const resetNetwork = async () => { + await network.provider.request({ method: "hardhat_reset", params: [] }) +} + +export const deployErc1820Registry = async (wallet: SignerWithAddress) => { + // deploy erc1820 registry + await ethers.provider.send("eth_sendTransaction", [ + { + from: wallet.address, + to: ERC1820_DEPLOYER, + value: "0x11c37937e080000", + }, + ]) + await ethers.provider.send("eth_sendRawTransaction", [ERC1820_PAYLOAD]) +} From f00af9045316fc977dabbfdf1387fa3c4ea10c64 Mon Sep 17 00:00:00 2001 From: Alain Olivier Date: Mon, 4 Dec 2023 19:40:19 +0100 Subject: [PATCH 02/19] refactor(evm): revise chain ID usage in pNetwork adapter --- .../adapters/pnetwork/PNetworkAdapter.sol | 9 +- .../pnetwork/PNetworkMessageRelayer.sol | 2 +- .../pnetwork/01_PNetworkMessageRelay.spec.ts | 34 +++++--- .../pnetwork/02_PNetworkAdapter.spec.ts | 84 ++++++------------- 4 files changed, 52 insertions(+), 77 deletions(-) diff --git a/packages/evm/contracts/adapters/pnetwork/PNetworkAdapter.sol b/packages/evm/contracts/adapters/pnetwork/PNetworkAdapter.sol index 73a06cfb..60fbe0d7 100644 --- a/packages/evm/contracts/adapters/pnetwork/PNetworkAdapter.sol +++ b/packages/evm/contracts/adapters/pnetwork/PNetworkAdapter.sol @@ -10,7 +10,6 @@ import "@openzeppelin/contracts-upgradeable/interfaces/IERC1820RegistryUpgradeab contract PNetworkAdapter is OracleAdapter, BlockHashOracleAdapter, IERC777Recipient { address public admittedSender; address public token; - bytes32 public chainId; IERC1820RegistryUpgradeable private constant _erc1820 = IERC1820RegistryUpgradeable(0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24); bytes32 private constant TOKENS_RECIPIENT_INTERFACE_HASH = keccak256("ERC777TokensRecipient"); @@ -19,10 +18,9 @@ contract PNetworkAdapter is OracleAdapter, BlockHashOracleAdapter, IERC777Recipi error ArrayLengthMismatch(); error InvalidSender(address sender); - constructor(address _sender, address _token, bytes32 _chainId) { + constructor(address _sender, address _token) { admittedSender = _sender; token = _token; - chainId = _chainId; _erc1820.setInterfaceImplementer(address(this), TOKENS_RECIPIENT_INTERFACE_HASH, address(this)); } @@ -37,7 +35,10 @@ contract PNetworkAdapter is OracleAdapter, BlockHashOracleAdapter, IERC777Recipi ) external override { require(msg.sender == address(token), "Only accepted token is allowed"); if (from != admittedSender) revert InvalidSender(from); - (uint256[] memory messageIds, bytes32[] memory hashes) = abi.decode(data, (uint256[], bytes32[])); + (uint256[] memory messageIds, bytes32[] memory hashes, uint256 chainId) = abi.decode( + data, + (uint256[], bytes32[], uint256) + ); if (messageIds.length != hashes.length) revert ArrayLengthMismatch(); for (uint256 i = 0; i < messageIds.length; i++) { _storeHash(uint256(chainId), messageIds[i], hashes[i]); diff --git a/packages/evm/contracts/adapters/pnetwork/PNetworkMessageRelayer.sol b/packages/evm/contracts/adapters/pnetwork/PNetworkMessageRelayer.sol index e6d7ffcf..6df727ff 100644 --- a/packages/evm/contracts/adapters/pnetwork/PNetworkMessageRelayer.sol +++ b/packages/evm/contracts/adapters/pnetwork/PNetworkMessageRelayer.sol @@ -50,7 +50,7 @@ contract PNetworkMessageRelay is IMessageRelay { hashes[i] = yaho.hashes(id); emit MessageRelayed(address(this), messageIds[i]); } - bytes memory userData = abi.encode(messageIds, hashes); + bytes memory userData = abi.encode(messageIds, hashes, block.chainid); if (vault != address(0)) { for (uint256 index = 0; index < _supportedNetworkIds.length; index++) { IERC20(token).approve(vault, SWAP_AMOUNT); diff --git a/packages/evm/test/adapters/pnetwork/01_PNetworkMessageRelay.spec.ts b/packages/evm/test/adapters/pnetwork/01_PNetworkMessageRelay.spec.ts index 8cc6f583..a03d6169 100644 --- a/packages/evm/test/adapters/pnetwork/01_PNetworkMessageRelay.spec.ts +++ b/packages/evm/test/adapters/pnetwork/01_PNetworkMessageRelay.spec.ts @@ -3,8 +3,6 @@ import { ethers, network } from "hardhat" import { ZERO_ADDRESS, deployErc1820Registry, resetNetwork } from "./utils.spec" -const DOMAIN_ID = "0x0000000000000000000000000000000000000000000000000000000000007a69" - describe("PNetworkMessageRelayer", function () { describe("Native Network", () => { const setup = async () => { @@ -27,7 +25,7 @@ describe("PNetworkMessageRelayer", function () { await erc777Token.connect(wallet).send(pNetworkMessageRelay.address, 10000, "0x") const PNetworkAdapter = await ethers.getContractFactory("PNetworkAdapter") - const pNetworkAdapter = await PNetworkAdapter.deploy(vault.address, pNetworkMessageRelay.address, DOMAIN_ID) + const pNetworkAdapter = await PNetworkAdapter.deploy(vault.address, pNetworkMessageRelay.address) const PingPong = await ethers.getContractFactory("PingPong") const pingPong = await PingPong.deploy() const message_1 = { @@ -71,8 +69,14 @@ describe("PNetworkMessageRelayer", function () { describe("relayMessages()", function () { it("Relays message hashes over pNetwork", async function () { - const { pNetworkMessageRelay, pNetworkAdapter, vault, erc777Token } = await setup() - await expect(pNetworkMessageRelay.relayMessages([0, 1], pNetworkAdapter.address)) + const { pNetworkMessageRelay, pNetworkAdapter, vault, erc777Token, yaho } = await setup() + const ids = [0, 1] + const hashes = await Promise.all(ids.map(async (_id) => await yaho.hashes(_id))) + const expectedUserData = new ethers.utils.AbiCoder().encode( + ["uint256[]", "bytes32[]", "uint256"], + [ids, hashes, network.config.chainId], + ) + await expect(pNetworkMessageRelay.relayMessages(ids, pNetworkAdapter.address)) .to.emit(pNetworkMessageRelay, "MessageRelayed") .withArgs(pNetworkMessageRelay.address, 0) .and.to.emit(vault, "PegIn") @@ -81,7 +85,7 @@ describe("PNetworkMessageRelayer", function () { pNetworkMessageRelay.address, 100, pNetworkAdapter.address.replace("0x", "").toLowerCase(), - "0x000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002fd0a21687761d1abc40ff03c81caca3fed1ee30a97ad6255c84706d3afe9fa2b92b1ce5792dbd6e0d8e27dc9819fee7d061c1836f6d145383057377bc24c9301", + expectedUserData, "0x12345678", "0x87654321", ) @@ -91,7 +95,7 @@ describe("PNetworkMessageRelayer", function () { pNetworkMessageRelay.address, 100, pNetworkAdapter.address.replace("0x", "").toLowerCase(), - "0x000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002fd0a21687761d1abc40ff03c81caca3fed1ee30a97ad6255c84706d3afe9fa2b92b1ce5792dbd6e0d8e27dc9819fee7d061c1836f6d145383057377bc24c9301", + expectedUserData, "0x12345678", "0x11223344", ) @@ -120,7 +124,7 @@ describe("PNetworkMessageRelayer", function () { await pToken.connect(wallet).send(pNetworkMessageRelay.address, 10000, "0x") const PNetworkAdapter = await ethers.getContractFactory("PNetworkAdapter") - const pNetworkAdapter = await PNetworkAdapter.deploy(vault.address, pNetworkMessageRelay.address, DOMAIN_ID) + const pNetworkAdapter = await PNetworkAdapter.deploy(vault.address, pNetworkMessageRelay.address) const PingPong = await ethers.getContractFactory("PingPong") const pingPong = await PingPong.deploy() const message_1 = { @@ -162,8 +166,14 @@ describe("PNetworkMessageRelayer", function () { describe("relayMessages()", function () { it("Relays message hashes over pNetwork", async function () { - const { pNetworkMessageRelay, pNetworkAdapter, pToken } = await setup() - await expect(pNetworkMessageRelay.relayMessages([0, 1], pNetworkAdapter.address)) + const { pNetworkMessageRelay, pNetworkAdapter, pToken, yaho } = await setup() + const ids = [0, 1] + const hashes = await Promise.all(ids.map(async (_id) => await yaho.hashes(_id))) + const expectedUserData = new ethers.utils.AbiCoder().encode( + ["uint256[]", "bytes32[]", "uint256"], + [ids, hashes, network.config.chainId], + ) + await expect(pNetworkMessageRelay.relayMessages(ids, pNetworkAdapter.address)) .to.emit(pNetworkMessageRelay, "MessageRelayed") .withArgs(pNetworkMessageRelay.address, 0) .and.to.emit(pToken, "Redeem") @@ -171,7 +181,7 @@ describe("PNetworkMessageRelayer", function () { pNetworkMessageRelay.address, 100, pNetworkAdapter.address.replace("0x", "").toLowerCase(), - "0x000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002fd0a21687761d1abc40ff03c81caca3fed1ee30a97ad6255c84706d3afe9fa2b92b1ce5792dbd6e0d8e27dc9819fee7d061c1836f6d145383057377bc24c9301", + expectedUserData, "0x87654321", "0x12345678", ) @@ -180,7 +190,7 @@ describe("PNetworkMessageRelayer", function () { pNetworkMessageRelay.address, 100, pNetworkAdapter.address.replace("0x", "").toLowerCase(), - "0x000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002fd0a21687761d1abc40ff03c81caca3fed1ee30a97ad6255c84706d3afe9fa2b92b1ce5792dbd6e0d8e27dc9819fee7d061c1836f6d145383057377bc24c9301", + expectedUserData, "0x87654321", "0x11223344", ) diff --git a/packages/evm/test/adapters/pnetwork/02_PNetworkAdapter.spec.ts b/packages/evm/test/adapters/pnetwork/02_PNetworkAdapter.spec.ts index ce315331..e647d038 100644 --- a/packages/evm/test/adapters/pnetwork/02_PNetworkAdapter.spec.ts +++ b/packages/evm/test/adapters/pnetwork/02_PNetworkAdapter.spec.ts @@ -19,20 +19,20 @@ describe("PNetworkAdapter", function () { const PToken = await ethers.getContractFactory("PToken") const pToken = await PToken.deploy("pToken", "P", []) const PNetworkAdapter = await ethers.getContractFactory("PNetworkAdapter") - const pNetworkAdapter = await PNetworkAdapter.deploy(ZERO_ADDRESS, pToken.address, DOMAIN_ID) + const pNetworkAdapter = await PNetworkAdapter.deploy(ZERO_ADDRESS, pToken.address) return { wallet, pNetworkAdapter, pToken, } } + describe("Constructor", function () { it("Successfully deploys contract with correct state", async function () { const { pNetworkAdapter, pToken } = await setup() expect(await pNetworkAdapter.deployed()) expect(await pNetworkAdapter.token()).to.equal(pToken.address) expect(await pNetworkAdapter.admittedSender()).to.equal(ZERO_ADDRESS) - expect(await pNetworkAdapter.chainId()).to.equal(DOMAIN_ID) }) }) @@ -42,11 +42,8 @@ describe("PNetworkAdapter", function () { const coder = new ethers.utils.AbiCoder() const data = coder.encode( - ["uint256[]", "bytes32[]"], - [ - [ID_ONE, ID_TWO], - [HASH_ONE, HASH_TWO], - ], + ["uint256[]", "bytes32[]", "uint256"], + [[ID_ONE, ID_TWO], [HASH_ONE, HASH_TWO], DOMAIN_ID], ) await pToken.connect(wallet).mint(pNetworkAdapter.address, 1000, data, "0x") expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_ONE)).to.equal(HASH_ONE) @@ -61,11 +58,8 @@ describe("PNetworkAdapter", function () { const coder = new ethers.utils.AbiCoder() const data = coder.encode( - ["uint256[]", "bytes32[]"], - [ - [ID_ONE, ID_TWO], - [HASH_ONE, HASH_TWO], - ], + ["uint256[]", "bytes32[]", "uint256"], + [[ID_ONE, ID_TWO], [HASH_ONE, HASH_TWO], DOMAIN_ID], ) await expect(fakePToken.connect(wallet).mint(pNetworkAdapter.address, 1000, data, "0x")).to.be.revertedWith( "Only accepted token is allowed", @@ -79,11 +73,8 @@ describe("PNetworkAdapter", function () { const coder = new ethers.utils.AbiCoder() const data = coder.encode( - ["uint256[]", "bytes32[]"], - [ - [ID_ONE, ID_TWO], - [HASH_ONE, HASH_TWO], - ], + ["uint256[]", "bytes32[]", "uint256"], + [[ID_ONE, ID_TWO], [HASH_ONE, HASH_TWO], DOMAIN_ID], ) await expect(pToken.connect(wallet).send(pNetworkAdapter.address, 1000, data)) .to.be.revertedWithCustomError(pNetworkAdapter, "InvalidSender") @@ -96,7 +87,7 @@ describe("PNetworkAdapter", function () { const { pNetworkAdapter, wallet, pToken } = await setup() const coder = new ethers.utils.AbiCoder() - const data = coder.encode(["uint256[]", "bytes32[]"], [[ID_ONE, ID_TWO], [HASH_ONE]]) + const data = coder.encode(["uint256[]", "bytes32[]", "uint256"], [[ID_ONE, ID_TWO], [HASH_ONE], DOMAIN_ID]) await expect(pToken.connect(wallet).mint(pNetworkAdapter.address, 1000, data, "0x")) .to.be.revertedWithCustomError(pNetworkAdapter, "ArrayLengthMismatch") .withArgs() @@ -108,22 +99,13 @@ describe("PNetworkAdapter", function () { const { pNetworkAdapter, wallet, pToken } = await setup() const coder = new ethers.utils.AbiCoder() let data = coder.encode( - ["uint256[]", "bytes32[]"], - [ - [ID_ONE, ID_TWO], - [HASH_ONE, HASH_TWO], - ], + ["uint256[]", "bytes32[]", "uint256"], + [[ID_ONE, ID_TWO], [HASH_ONE, HASH_TWO], DOMAIN_ID], ) await pToken.connect(wallet).mint(pNetworkAdapter.address, 1000, data, "0x") expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_ONE)).to.equal(HASH_ONE) expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_TWO)).to.equal(HASH_TWO) - data = coder.encode( - ["uint256[]", "bytes32[]"], - [ - [ID_TWO, ID_ONE], - [HASH_ONE, HASH_TWO], - ], - ) + data = coder.encode(["uint256[]", "bytes32[]", "uint256"], [[ID_TWO, ID_ONE], [HASH_ONE, HASH_TWO], DOMAIN_ID]) await pToken.connect(wallet).mint(pNetworkAdapter.address, 1000, data, "0x") expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_ONE)).to.equal(HASH_TWO) expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_TWO)).to.equal(HASH_ONE) @@ -148,7 +130,7 @@ describe("PNetworkAdapter", function () { const vault = await Vault.deploy() await vault.initialize([erc777Token.address, anotherErc777Token.address], "0x12345678") const PNetworkAdapter = await ethers.getContractFactory("PNetworkAdapter") - const pNetworkAdapter = await PNetworkAdapter.deploy(vault.address, erc777Token.address, DOMAIN_ID) + const pNetworkAdapter = await PNetworkAdapter.deploy(vault.address, erc777Token.address) const coder = new ethers.utils.AbiCoder() const data = coder.encode( ["bytes32", "string", "bytes4"], @@ -164,13 +146,13 @@ describe("PNetworkAdapter", function () { anotherErc777Token, } } + describe("Constructor", function () { it("Successfully deploys contract with correct state", async function () { const { pNetworkAdapter, erc777Token, vault } = await setup() expect(await pNetworkAdapter.deployed()) expect(await pNetworkAdapter.token()).to.equal(erc777Token.address) expect(await pNetworkAdapter.admittedSender()).to.equal(vault.address) - expect(await pNetworkAdapter.chainId()).to.equal(DOMAIN_ID) }) }) @@ -180,11 +162,8 @@ describe("PNetworkAdapter", function () { const coder = new ethers.utils.AbiCoder() const data = coder.encode( - ["uint256[]", "bytes32[]"], - [ - [ID_ONE, ID_TWO], - [HASH_ONE, HASH_TWO], - ], + ["uint256[]", "bytes32[]", "uint256"], + [[ID_ONE, ID_TWO], [HASH_ONE, HASH_TWO], DOMAIN_ID], ) await vault.connect(wallet).pegOut(pNetworkAdapter.address, erc777Token.address, 100, data) expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_ONE)).to.equal(HASH_ONE) @@ -196,11 +175,8 @@ describe("PNetworkAdapter", function () { const coder = new ethers.utils.AbiCoder() const data = coder.encode( - ["uint256[]", "bytes32[]"], - [ - [ID_ONE, ID_TWO], - [HASH_ONE, HASH_TWO], - ], + ["uint256[]", "bytes32[]", "uint256"], + [[ID_ONE, ID_TWO], [HASH_ONE, HASH_TWO], DOMAIN_ID], ) await expect( vault.connect(wallet).pegOut(pNetworkAdapter.address, anotherErc777Token.address, 100, data), @@ -214,11 +190,8 @@ describe("PNetworkAdapter", function () { const coder = new ethers.utils.AbiCoder() const data = coder.encode( - ["uint256[]", "bytes32[]"], - [ - [ID_ONE, ID_TWO], - [HASH_ONE, HASH_TWO], - ], + ["uint256[]", "bytes32[]", "uint256"], + [[ID_ONE, ID_TWO], [HASH_ONE, HASH_TWO], DOMAIN_ID], ) await expect(erc777Token.connect(wallet).send(pNetworkAdapter.address, 100, data)) .to.be.revertedWithCustomError(pNetworkAdapter, "InvalidSender") @@ -230,7 +203,7 @@ describe("PNetworkAdapter", function () { it("Should not store hashes when parallel arrays are mismatched", async function () { const { pNetworkAdapter, wallet, erc777Token, vault } = await setup() const coder = new ethers.utils.AbiCoder() - const data = coder.encode(["uint256[]", "bytes32[]"], [[ID_ONE, ID_TWO], [HASH_ONE]]) + const data = coder.encode(["uint256[]", "bytes32[]", "uint256"], [[ID_ONE, ID_TWO], [HASH_ONE], DOMAIN_ID]) await expect(vault.connect(wallet).pegOut(pNetworkAdapter.address, erc777Token.address, 100, data)) .to.be.revertedWithCustomError(pNetworkAdapter, "ArrayLengthMismatch") .withArgs() @@ -242,22 +215,13 @@ describe("PNetworkAdapter", function () { const { pNetworkAdapter, wallet, erc777Token, vault } = await setup() const coder = new ethers.utils.AbiCoder() let data = coder.encode( - ["uint256[]", "bytes32[]"], - [ - [ID_ONE, ID_TWO], - [HASH_ONE, HASH_TWO], - ], + ["uint256[]", "bytes32[]", "uint256"], + [[ID_ONE, ID_TWO], [HASH_ONE, HASH_TWO], DOMAIN_ID], ) await vault.connect(wallet).pegOut(pNetworkAdapter.address, erc777Token.address, 50, data) expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_ONE)).to.equal(HASH_ONE) expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_TWO)).to.equal(HASH_TWO) - data = coder.encode( - ["uint256[]", "bytes32[]"], - [ - [ID_TWO, ID_ONE], - [HASH_ONE, HASH_TWO], - ], - ) + data = coder.encode(["uint256[]", "bytes32[]", "uint256"], [[ID_TWO, ID_ONE], [HASH_ONE, HASH_TWO], DOMAIN_ID]) await vault.connect(wallet).pegOut(pNetworkAdapter.address, erc777Token.address, 50, data) expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_ONE)).to.equal(HASH_TWO) expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_TWO)).to.equal(HASH_ONE) From 1353f86699ff357decebf985d6d2a872f0a55055 Mon Sep 17 00:00:00 2001 From: Alain Olivier Date: Mon, 4 Dec 2023 19:43:35 +0100 Subject: [PATCH 03/19] chore(evm): add deploy tasks for pNetwork adapter and relayer --- packages/evm/tasks/deploy/adapters/index.ts | 1 + .../evm/tasks/deploy/adapters/pnetwork.ts | 48 +++++++++++++++++++ 2 files changed, 49 insertions(+) create mode 100644 packages/evm/tasks/deploy/adapters/pnetwork.ts diff --git a/packages/evm/tasks/deploy/adapters/index.ts b/packages/evm/tasks/deploy/adapters/index.ts index a5ba233c..4545e873 100644 --- a/packages/evm/tasks/deploy/adapters/index.ts +++ b/packages/evm/tasks/deploy/adapters/index.ts @@ -8,6 +8,7 @@ import "./electron" import "./hyperlane" import "./layerzero" import "./optimism" +import "./pnetwork" import "./sygma" import "./telepathy" import "./wormhole" diff --git a/packages/evm/tasks/deploy/adapters/pnetwork.ts b/packages/evm/tasks/deploy/adapters/pnetwork.ts new file mode 100644 index 00000000..bdebf154 --- /dev/null +++ b/packages/evm/tasks/deploy/adapters/pnetwork.ts @@ -0,0 +1,48 @@ +import type { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers" +import { task } from "hardhat/config" +import type { TaskArguments } from "hardhat/types" + +import type { PNetworkAdapter } from "../../../types/contracts/adapters/pnetwork/PNetworkAdapter" +import type { PNetworkMessageRelay } from "../../../types/contracts/adapters/pnetwork/PNetworkMessageRelayer.sol" +import type { PNetworkAdapter__factory } from "../../../types/factories/contracts/adapters/pnetwork/PNetworkAdapter__factory" +import type { PNetworkMessageRelay__factory } from "../../../types/factories/contracts/adapters/pnetwork/PNetworkMessageRelayer.sol" +import { verify } from "../index" + +task("deploy:adapter:PNetworkMessageRelay") + .addParam("vault", "address of the vault contract (address 0 when deploying on non-native chain)") + .addParam("token", "address of the token used to transfer data") + .addParam("yaho", "address of the Yaho contract") + + .setAction(async function (taskArguments: TaskArguments, hre) { + console.log("Deploying PNetworkMessageRelay...") + const signers: SignerWithAddress[] = await hre.ethers.getSigners() + const pNetworkAdapterFactory: PNetworkMessageRelay__factory = ( + await hre.ethers.getContractFactory("PNetworkMessageRelay") + ) + const constructorArguments = [taskArguments.vault, taskArguments.token, taskArguments.yaho] as const + const pNetworkMessageRelay: PNetworkMessageRelay = ( + await pNetworkAdapterFactory.connect(signers[0]).deploy(...constructorArguments) + ) + await pNetworkMessageRelay.deployed() + console.log("PNetworkMessageRelay deployed to:", pNetworkMessageRelay.address) + + if (taskArguments.verify) await verify(hre, pNetworkMessageRelay, constructorArguments) + }) + +task("deploy:adapter:PNetworkAdapter") + .addParam("vault", "address of the vault contract (address 0 when deploying on non-native chain)") + .addParam("token", "address of the token used to transfer data") + .setAction(async function (taskArguments: TaskArguments, hre) { + console.log("Deploying PNetworkAdapter...") + const signers: SignerWithAddress[] = await hre.ethers.getSigners() + const pNetworkAdapterFactory: PNetworkAdapter__factory = ( + await hre.ethers.getContractFactory("TelepathyAdapter") + ) + const constructorArguments = [taskArguments.vaultm, taskArguments.token] as const + const pNetworkAdapter: PNetworkAdapter = ( + await pNetworkAdapterFactory.connect(signers[0]).deploy(...constructorArguments) + ) + await pNetworkAdapter.deployed() + console.log("PNetworkAdapter deployed to:", pNetworkAdapter.address) + if (taskArguments.verify) await verify(hre, pNetworkAdapter, constructorArguments) + }) From 629db01018f00cff25e439f647141c242a9c6b95 Mon Sep 17 00:00:00 2001 From: Alain Olivier Date: Tue, 5 Dec 2023 15:30:36 +0100 Subject: [PATCH 04/19] feat(evm): add pNetwork header reporter --- .../adapters/pnetwork/PNetworkAdapter.sol | 51 +++---- .../adapters/pnetwork/PNetworkBase.sol | 24 ++++ .../pnetwork/PNetworkHeaderReporter.sol | 19 +++ .../pnetwork/PNetworkMessageRelayer.sol | 131 ++---------------- .../adapters/pnetwork/PNetworkReporter.sol | 74 ++++++++++ .../pnetwork/{ => interfaces}/IErc20Vault.sol | 0 .../pnetwork/{ => interfaces}/IPToken.sol | 0 .../evm/tasks/deploy/adapters/pnetwork.ts | 55 +++++++- .../pnetwork/01_PNetworkMessageRelay.spec.ts | 104 ++++++-------- .../pnetwork/02_PNetworkAdapter.spec.ts | 126 ++++++++++------- 10 files changed, 325 insertions(+), 259 deletions(-) create mode 100644 packages/evm/contracts/adapters/pnetwork/PNetworkBase.sol create mode 100644 packages/evm/contracts/adapters/pnetwork/PNetworkHeaderReporter.sol create mode 100644 packages/evm/contracts/adapters/pnetwork/PNetworkReporter.sol rename packages/evm/contracts/adapters/pnetwork/{ => interfaces}/IErc20Vault.sol (100%) rename packages/evm/contracts/adapters/pnetwork/{ => interfaces}/IPToken.sol (100%) diff --git a/packages/evm/contracts/adapters/pnetwork/PNetworkAdapter.sol b/packages/evm/contracts/adapters/pnetwork/PNetworkAdapter.sol index 60fbe0d7..bf8488c6 100644 --- a/packages/evm/contracts/adapters/pnetwork/PNetworkAdapter.sol +++ b/packages/evm/contracts/adapters/pnetwork/PNetworkAdapter.sol @@ -1,47 +1,40 @@ // SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.17; -import { OracleAdapter } from "../OracleAdapter.sol"; -import { BlockHashOracleAdapter } from "../BlockHashOracleAdapter.sol"; - import "@openzeppelin/contracts/token/ERC777/presets/ERC777PresetFixedSupply.sol"; -import "@openzeppelin/contracts-upgradeable/interfaces/IERC1820RegistryUpgradeable.sol"; -contract PNetworkAdapter is OracleAdapter, BlockHashOracleAdapter, IERC777Recipient { - address public admittedSender; - address public token; - IERC1820RegistryUpgradeable private constant _erc1820 = - IERC1820RegistryUpgradeable(0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24); - bytes32 private constant TOKENS_RECIPIENT_INTERFACE_HASH = keccak256("ERC777TokensRecipient"); - bytes32 private constant Erc777Token_INTERFACE_HASH = keccak256("ERC777Token"); +import { HeaderOracleAdapter } from "../HeaderOracleAdapter.sol"; +import { PNetworkBase } from "./PNetworkBase.sol"; + +contract PNetworkAdapter is HeaderOracleAdapter, PNetworkBase { + address public immutable ADMITTED_SENDER; + address public immutable TOKEN; error ArrayLengthMismatch(); error InvalidSender(address sender); - constructor(address _sender, address _token) { - admittedSender = _sender; - token = _token; - _erc1820.setInterfaceImplementer(address(this), TOKENS_RECIPIENT_INTERFACE_HASH, address(this)); + constructor( + uint256 reporterChain, + address reporterAddress, + address pNetworkAdmittedsender, + address pNetworkToken, + uint32 pNetworkReporterChain + ) HeaderOracleAdapter(reporterChain, reporterAddress) PNetworkBase(pNetworkReporterChain) { + ADMITTED_SENDER = pNetworkAdmittedsender; + TOKEN = pNetworkToken; } // Implement the ERC777TokensRecipient interface function tokensReceived( - address operator, + address, address from, - address to, - uint256 amount, + address, + uint256, bytes calldata data, - bytes calldata operatorData + bytes calldata ) external override { - require(msg.sender == address(token), "Only accepted token is allowed"); - if (from != admittedSender) revert InvalidSender(from); - (uint256[] memory messageIds, bytes32[] memory hashes, uint256 chainId) = abi.decode( - data, - (uint256[], bytes32[], uint256) - ); - if (messageIds.length != hashes.length) revert ArrayLengthMismatch(); - for (uint256 i = 0; i < messageIds.length; i++) { - _storeHash(uint256(chainId), messageIds[i], hashes[i]); - } + require(msg.sender == address(TOKEN), "Only accepted token is allowed"); + if (from != ADMITTED_SENDER) revert InvalidSender(from); + _receivePayload(data); } } diff --git a/packages/evm/contracts/adapters/pnetwork/PNetworkBase.sol b/packages/evm/contracts/adapters/pnetwork/PNetworkBase.sol new file mode 100644 index 00000000..4da75770 --- /dev/null +++ b/packages/evm/contracts/adapters/pnetwork/PNetworkBase.sol @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: LGPL-3.0-only +pragma solidity ^0.8.17; + +import "@openzeppelin/contracts/interfaces/IERC777Recipient.sol"; +import "@openzeppelin/contracts-upgradeable/interfaces/IERC1820RegistryUpgradeable.sol"; + +abstract contract PNetworkBase is IERC777Recipient { + mapping(uint256 => bytes4) public SUPPORTED_NETWORK_IDS; + uint256 public immutable PNETWORK_REF_CHAIN; + IERC1820RegistryUpgradeable private constant ERC1820 = + IERC1820RegistryUpgradeable(0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24); + bytes32 private constant TOKENS_RECIPIENT_INTERFACE_HASH = keccak256("ERC777TokensRecipient"); + + constructor(uint256 pNetworkRefChain) { + SUPPORTED_NETWORK_IDS[1] = 0x005fe7f9; // EthereumMainnet + SUPPORTED_NETWORK_IDS[64] = 0x00e4b170; // BscMainnet + SUPPORTED_NETWORK_IDS[100] = 0x00f1918e; // GnosisMainnet + SUPPORTED_NETWORK_IDS[137] = 0x0075dd4c; // PolygonMainnet + SUPPORTED_NETWORK_IDS[42161] = 0x00ce98c4; // ArbitrumMainnet + require(SUPPORTED_NETWORK_IDS[pNetworkRefChain] != 0, "Not supported chain"); + PNETWORK_REF_CHAIN = pNetworkRefChain; + ERC1820.setInterfaceImplementer(address(this), TOKENS_RECIPIENT_INTERFACE_HASH, address(this)); + } +} diff --git a/packages/evm/contracts/adapters/pnetwork/PNetworkHeaderReporter.sol b/packages/evm/contracts/adapters/pnetwork/PNetworkHeaderReporter.sol new file mode 100644 index 00000000..9e923a0d --- /dev/null +++ b/packages/evm/contracts/adapters/pnetwork/PNetworkHeaderReporter.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: LGPL-3.0-only +pragma solidity ^0.8.17; + +import { HeaderReporter } from "../HeaderReporter.sol"; +import { PNetworkReporter } from "./PNetworkReporter.sol"; + +contract PNetworkHeaderReporter is HeaderReporter, PNetworkReporter { + constructor( + address headerStorage, + uint64 adapterChain, + address vault, + address token, + uint32 pNetworkAdapterChain + ) HeaderReporter(headerStorage, adapterChain) PNetworkReporter(vault, token, pNetworkAdapterChain) {} // solhint-disable no-empty-blocks + + function _sendPayload(bytes memory payload, address adapter) internal override { + _pNetworkSend(payload, adapter); + } +} diff --git a/packages/evm/contracts/adapters/pnetwork/PNetworkMessageRelayer.sol b/packages/evm/contracts/adapters/pnetwork/PNetworkMessageRelayer.sol index 6df727ff..444c0522 100644 --- a/packages/evm/contracts/adapters/pnetwork/PNetworkMessageRelayer.sol +++ b/packages/evm/contracts/adapters/pnetwork/PNetworkMessageRelayer.sol @@ -1,122 +1,19 @@ // SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.17; -import { IMessageRelay } from "../../interfaces/IMessageRelay.sol"; -import { IErc20Vault } from "./IErc20Vault.sol"; -import { IPToken } from "./IPToken.sol"; -import { Yaho } from "../../Yaho.sol"; -import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import "@openzeppelin/contracts-upgradeable/utils/introspection/IERC1820RegistryUpgradeable.sol"; -import "@openzeppelin/contracts/token/ERC777/IERC777.sol"; - -contract PNetworkMessageRelay is IMessageRelay { - bytes32 private constant TOKENS_RECIPIENT_INTERFACE_HASH = keccak256("ERC777TokensRecipient"); - IERC1820RegistryUpgradeable private constant _erc1820 = - IERC1820RegistryUpgradeable(0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24); - - address public immutable token; - address public immutable vault; - Yaho public immutable yaho; - bytes4[] private _supportedNetworkIds; - - uint256 private constant SWAP_AMOUNT = 100; - - event MessageRelayed(address indexed emitter, uint256 indexed messageId); - event NetworkIdAdded(bytes4 networkId); - - error AlreadyExistingNetworkId(bytes4 networkId); - - constructor(address _vault, address _token, Yaho _yaho) { - vault = _vault; - token = _token; - yaho = _yaho; - _erc1820.setInterfaceImplementer(address(this), TOKENS_RECIPIENT_INTERFACE_HASH, address(this)); - } - - // TODO: limit access to this function - function addNetwork(bytes4 networkId) public { - if (_isNetworkSupported(networkId)) revert AlreadyExistingNetworkId(networkId); - _supportedNetworkIds.push(networkId); - emit NetworkIdAdded(networkId); - } - - function relayMessages( - uint256[] memory messageIds, - address pnetworkAdapter - ) public payable returns (bytes32 receipt) { - bytes32[] memory hashes = new bytes32[](messageIds.length); - for (uint256 i = 0; i < messageIds.length; i++) { - uint256 id = messageIds[i]; - hashes[i] = yaho.hashes(id); - emit MessageRelayed(address(this), messageIds[i]); - } - bytes memory userData = abi.encode(messageIds, hashes, block.chainid); - if (vault != address(0)) { - for (uint256 index = 0; index < _supportedNetworkIds.length; index++) { - IERC20(token).approve(vault, SWAP_AMOUNT); - IErc20Vault(vault).pegIn( - SWAP_AMOUNT, - token, - _toAsciiString(pnetworkAdapter), - userData, - _supportedNetworkIds[index] - ); - } - } else { - for (uint256 index = 0; index < _supportedNetworkIds.length; index++) { - IPToken(token).redeem( - SWAP_AMOUNT, - userData, - _toAsciiString(pnetworkAdapter), - _supportedNetworkIds[index] - ); - } - } - // TODO: return something resembling a receipt - } - - /** - * @dev Implementation of IERC777Recipient. - */ - function tokensReceived( - address /*operator*/, - address from, - address to, - uint256 amount, - bytes calldata userData, - bytes calldata /*operatorData*/ - ) external onlySupportedToken(msg.sender) { - require(to == address(this), "Token receiver is not this contract"); - } - - modifier onlySupportedToken(address _tokenAddress) { - require(_tokenAddress == token, "Token at supplied address is NOT supported!"); - _; - } - - function _char(bytes1 b) internal pure returns (bytes1 c) { - if (uint8(b) < 10) return bytes1(uint8(b) + 0x30); - else return bytes1(uint8(b) + 0x57); - } - - function _isNetworkSupported(bytes4 _target) internal view returns (bool) { - for (uint i = 0; i < _supportedNetworkIds.length; i++) { - if (_supportedNetworkIds[i] == _target) { - return true; // Value found in the array - } - } - return false; // Value not found in the array - } - - function _toAsciiString(address x) internal pure returns (string memory) { - bytes memory s = new bytes(40); - for (uint i = 0; i < 20; i++) { - bytes1 b = bytes1(uint8(uint(uint160(x)) / (2 ** (8 * (19 - i))))); - bytes1 hi = bytes1(uint8(b) / 16); - bytes1 lo = bytes1(uint8(b) - 16 * uint8(hi)); - s[2 * i] = _char(hi); - s[2 * i + 1] = _char(lo); - } - return string(s); +import { MessageRelay } from "../MessageRelay.sol"; +import { PNetworkReporter } from "./PNetworkReporter.sol"; + +contract PNetworkMessageRelay is MessageRelay, PNetworkReporter { + constructor( + address yaho, + uint64 adapterChain, + address vault, + address token, + uint32 pNetworkAdapterChain + ) MessageRelay(yaho, adapterChain) PNetworkReporter(vault, token, pNetworkAdapterChain) {} // solhint-disable no-empty-blocks + + function _sendPayload(bytes memory payload, address adapter) internal override { + _pNetworkSend(payload, adapter); } } diff --git a/packages/evm/contracts/adapters/pnetwork/PNetworkReporter.sol b/packages/evm/contracts/adapters/pnetwork/PNetworkReporter.sol new file mode 100644 index 00000000..8b43cad5 --- /dev/null +++ b/packages/evm/contracts/adapters/pnetwork/PNetworkReporter.sol @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: LGPL-3.0-only +pragma solidity ^0.8.17; + +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; + +import { IErc20Vault } from "./interfaces/IErc20Vault.sol"; +import { IPToken } from "./interfaces/IPToken.sol"; +import { PNetworkBase } from "./PNetworkBase.sol"; + +abstract contract PNetworkReporter is PNetworkBase { + uint256 private constant SWAP_AMOUNT = 100; + + address public immutable VAULT; + address public immutable TOKEN; + + constructor(address vault, address token, uint256 pNetworkAdapterChain) PNetworkBase(pNetworkAdapterChain) { + VAULT = vault; + TOKEN = token; + } + + // Implement the ERC777TokensRecipient interface + function tokensReceived( + address, + address, + address to, + uint256, + bytes calldata, + bytes calldata + ) external view onlySupportedToken(msg.sender) { + require(to == address(this), "Token receiver is not this contract"); + } + + modifier onlySupportedToken(address _tokenAddress) { + require(_tokenAddress == TOKEN, "Token at supplied address is NOT supported!"); + _; + } + + function _char(bytes1 b) internal pure returns (bytes1 c) { + if (uint8(b) < 10) return bytes1(uint8(b) + 0x30); + else return bytes1(uint8(b) + 0x57); + } + + function _pNetworkSend(bytes memory payload, address adapter) internal { + if (VAULT != address(0)) { + IERC20(TOKEN).approve(VAULT, SWAP_AMOUNT); + IErc20Vault(VAULT).pegIn( + SWAP_AMOUNT, + TOKEN, + _toAsciiString(adapter), + payload, + SUPPORTED_NETWORK_IDS[PNETWORK_REF_CHAIN] + ); + } else { + IPToken(TOKEN).redeem( + SWAP_AMOUNT, + payload, + _toAsciiString(adapter), + SUPPORTED_NETWORK_IDS[PNETWORK_REF_CHAIN] + ); + } + } + + function _toAsciiString(address x) internal pure returns (string memory) { + bytes memory s = new bytes(40); + for (uint i = 0; i < 20; i++) { + bytes1 b = bytes1(uint8(uint(uint160(x)) / (2 ** (8 * (19 - i))))); + bytes1 hi = bytes1(uint8(b) / 16); + bytes1 lo = bytes1(uint8(b) - 16 * uint8(hi)); + s[2 * i] = _char(hi); + s[2 * i + 1] = _char(lo); + } + return string(s); + } +} diff --git a/packages/evm/contracts/adapters/pnetwork/IErc20Vault.sol b/packages/evm/contracts/adapters/pnetwork/interfaces/IErc20Vault.sol similarity index 100% rename from packages/evm/contracts/adapters/pnetwork/IErc20Vault.sol rename to packages/evm/contracts/adapters/pnetwork/interfaces/IErc20Vault.sol diff --git a/packages/evm/contracts/adapters/pnetwork/IPToken.sol b/packages/evm/contracts/adapters/pnetwork/interfaces/IPToken.sol similarity index 100% rename from packages/evm/contracts/adapters/pnetwork/IPToken.sol rename to packages/evm/contracts/adapters/pnetwork/interfaces/IPToken.sol diff --git a/packages/evm/tasks/deploy/adapters/pnetwork.ts b/packages/evm/tasks/deploy/adapters/pnetwork.ts index bdebf154..7b4cd9fe 100644 --- a/packages/evm/tasks/deploy/adapters/pnetwork.ts +++ b/packages/evm/tasks/deploy/adapters/pnetwork.ts @@ -3,23 +3,60 @@ import { task } from "hardhat/config" import type { TaskArguments } from "hardhat/types" import type { PNetworkAdapter } from "../../../types/contracts/adapters/pnetwork/PNetworkAdapter" +import type { PNetworkHeaderReporter } from "../../../types/contracts/adapters/pnetwork/PNetworkHeaderReporter" import type { PNetworkMessageRelay } from "../../../types/contracts/adapters/pnetwork/PNetworkMessageRelayer.sol" import type { PNetworkAdapter__factory } from "../../../types/factories/contracts/adapters/pnetwork/PNetworkAdapter__factory" +import type { PNetworkHeaderReporter__factory } from "../../../types/factories/contracts/adapters/pnetwork/PNetworkHeaderReporter__factory" import type { PNetworkMessageRelay__factory } from "../../../types/factories/contracts/adapters/pnetwork/PNetworkMessageRelayer.sol" import { verify } from "../index" -task("deploy:adapter:PNetworkMessageRelay") +task("deploy:adapter:PNetworkHeaderReporter") + .addParam("chainId", "chain id of the adapter contract") + .addParam("yaho", "address of the Yaho contract") .addParam("vault", "address of the vault contract (address 0 when deploying on non-native chain)") .addParam("token", "address of the token used to transfer data") - .addParam("yaho", "address of the Yaho contract") + .addFlag("verify", "whether to verify the contract on Etherscan") + .setAction(async function (taskArguments: TaskArguments, hre) { + console.log("Deploying PNetworkHeaderReporter...") + const signers: SignerWithAddress[] = await hre.ethers.getSigners() + const pNetworkHeaderReporterFactory: PNetworkHeaderReporter__factory = ( + await hre.ethers.getContractFactory("PNetworkHeaderReporter") + ) + const constructorArguments = [ + taskArguments.yaho, + taskArguments.chainId, + taskArguments.vault, + taskArguments.token, + taskArguments.chainId, + ] as const + const pNetworkHeaderReporter: PNetworkHeaderReporter = ( + await pNetworkHeaderReporterFactory.connect(signers[0]).deploy(...constructorArguments) + ) + await pNetworkHeaderReporter.deployed() + console.log("PNetworkHeaderReporter deployed to:", pNetworkHeaderReporter.address) + if (taskArguments.verify) await verify(hre, pNetworkHeaderReporter, constructorArguments) + }) + +task("deploy:adapter:PNetworkMessageRelay") + .addParam("chainId", "chain id of the adapter contract") + .addParam("yaho", "address of the Yaho contract") + .addParam("vault", "address of the vault contract (address 0 when deploying on non-native chain)") + .addParam("token", "address of the token used to transfer data") + .addFlag("verify", "whether to verify the contract on Etherscan") .setAction(async function (taskArguments: TaskArguments, hre) { console.log("Deploying PNetworkMessageRelay...") const signers: SignerWithAddress[] = await hre.ethers.getSigners() const pNetworkAdapterFactory: PNetworkMessageRelay__factory = ( await hre.ethers.getContractFactory("PNetworkMessageRelay") ) - const constructorArguments = [taskArguments.vault, taskArguments.token, taskArguments.yaho] as const + const constructorArguments = [ + taskArguments.yaho, + taskArguments.chainId, + taskArguments.vault, + taskArguments.token, + taskArguments.chainId, + ] as const const pNetworkMessageRelay: PNetworkMessageRelay = ( await pNetworkAdapterFactory.connect(signers[0]).deploy(...constructorArguments) ) @@ -30,19 +67,29 @@ task("deploy:adapter:PNetworkMessageRelay") }) task("deploy:adapter:PNetworkAdapter") + .addParam("chainId", "chain id of the reporter contract") + .addParam("reporter", "address of the reporter contract") .addParam("vault", "address of the vault contract (address 0 when deploying on non-native chain)") .addParam("token", "address of the token used to transfer data") + .addFlag("verify", "whether to verify the contract on Etherscan") .setAction(async function (taskArguments: TaskArguments, hre) { console.log("Deploying PNetworkAdapter...") const signers: SignerWithAddress[] = await hre.ethers.getSigners() const pNetworkAdapterFactory: PNetworkAdapter__factory = ( await hre.ethers.getContractFactory("TelepathyAdapter") ) - const constructorArguments = [taskArguments.vaultm, taskArguments.token] as const + const constructorArguments = [ + taskArguments.chainIdm, + taskArguments.reporter, + taskArguments.vaultm, + taskArguments.token, + taskArguments.chainId, + ] as const const pNetworkAdapter: PNetworkAdapter = ( await pNetworkAdapterFactory.connect(signers[0]).deploy(...constructorArguments) ) await pNetworkAdapter.deployed() console.log("PNetworkAdapter deployed to:", pNetworkAdapter.address) + if (taskArguments.verify) await verify(hre, pNetworkAdapter, constructorArguments) }) diff --git a/packages/evm/test/adapters/pnetwork/01_PNetworkMessageRelay.spec.ts b/packages/evm/test/adapters/pnetwork/01_PNetworkMessageRelay.spec.ts index a03d6169..d6fae458 100644 --- a/packages/evm/test/adapters/pnetwork/01_PNetworkMessageRelay.spec.ts +++ b/packages/evm/test/adapters/pnetwork/01_PNetworkMessageRelay.spec.ts @@ -1,8 +1,10 @@ import { expect } from "chai" -import { ethers, network } from "hardhat" +import { ethers } from "hardhat" import { ZERO_ADDRESS, deployErc1820Registry, resetNetwork } from "./utils.spec" +const DOMAIN_ID = "0x0000000000000000000000000000000000000000000000000000000000001" + describe("PNetworkMessageRelayer", function () { describe("Native Network", () => { const setup = async () => { @@ -17,15 +19,27 @@ describe("PNetworkMessageRelayer", function () { const Vault = await ethers.getContractFactory("MockVault") const vault = await Vault.deploy() await vault.initialize([erc777Token.address, anotherErc777Token.address], "0x12345678") + const PNetworkMessageRelay = await ethers.getContractFactory("PNetworkMessageRelay") - const pNetworkMessageRelay = await PNetworkMessageRelay.deploy(vault.address, erc777Token.address, yaho.address) - await pNetworkMessageRelay.addNetwork("0x87654321") - await pNetworkMessageRelay.addNetwork("0x11223344") + const pNetworkMessageRelay = await PNetworkMessageRelay.deploy( + yaho.address, + DOMAIN_ID, + vault.address, + erc777Token.address, + DOMAIN_ID, + ) await erc777Token.connect(wallet).send(pNetworkMessageRelay.address, 10000, "0x") const PNetworkAdapter = await ethers.getContractFactory("PNetworkAdapter") - const pNetworkAdapter = await PNetworkAdapter.deploy(vault.address, pNetworkMessageRelay.address) + const pNetworkAdapter = await PNetworkAdapter.deploy( + 1, + pNetworkMessageRelay.address, + vault.address, + pNetworkMessageRelay.address, + 1, + ) + const PingPong = await ethers.getContractFactory("PingPong") const pingPong = await PingPong.deploy() const message_1 = { @@ -53,17 +67,9 @@ describe("PNetworkMessageRelayer", function () { it("Successfully deploys contract", async function () { const { pNetworkMessageRelay, erc777Token, vault, yaho } = await setup() expect(await pNetworkMessageRelay.deployed()) - expect(await pNetworkMessageRelay.token()).to.equal(erc777Token.address) - expect(await pNetworkMessageRelay.vault()).to.equal(vault.address) - expect(await pNetworkMessageRelay.yaho()).to.equal(yaho.address) - }) - - it("Should not permit to add again the same network", async function () { - const { pNetworkMessageRelay } = await setup() - await expect(pNetworkMessageRelay.addNetwork("0x11223344")).to.revertedWithCustomError( - pNetworkMessageRelay, - "AlreadyExistingNetworkId", - ) + expect(await pNetworkMessageRelay.TOKEN()).to.equal(erc777Token.address) + expect(await pNetworkMessageRelay.VAULT()).to.equal(vault.address) + expect(await pNetworkMessageRelay.YAHO()).to.equal(yaho.address) }) }) @@ -72,10 +78,7 @@ describe("PNetworkMessageRelayer", function () { const { pNetworkMessageRelay, pNetworkAdapter, vault, erc777Token, yaho } = await setup() const ids = [0, 1] const hashes = await Promise.all(ids.map(async (_id) => await yaho.hashes(_id))) - const expectedUserData = new ethers.utils.AbiCoder().encode( - ["uint256[]", "bytes32[]", "uint256"], - [ids, hashes, network.config.chainId], - ) + const expectedUserData = new ethers.utils.AbiCoder().encode(["uint256[]", "bytes32[]"], [ids, hashes]) await expect(pNetworkMessageRelay.relayMessages(ids, pNetworkAdapter.address)) .to.emit(pNetworkMessageRelay, "MessageRelayed") .withArgs(pNetworkMessageRelay.address, 0) @@ -87,17 +90,7 @@ describe("PNetworkMessageRelayer", function () { pNetworkAdapter.address.replace("0x", "").toLowerCase(), expectedUserData, "0x12345678", - "0x87654321", - ) - .and.to.emit(vault, "PegIn") - .withArgs( - erc777Token.address, - pNetworkMessageRelay.address, - 100, - pNetworkAdapter.address.replace("0x", "").toLowerCase(), - expectedUserData, - "0x12345678", - "0x11223344", + "0x005fe7f9", ) }) }) @@ -116,15 +109,26 @@ describe("PNetworkMessageRelayer", function () { const Vault = await ethers.getContractFactory("MockVault") const vault = await Vault.deploy() await vault.initialize([pToken.address, anotherPToken.address], "0x12345678") + const PNetworkMessageRelay = await ethers.getContractFactory("PNetworkMessageRelay") - const pNetworkMessageRelay = await PNetworkMessageRelay.deploy(ZERO_ADDRESS, pToken.address, yaho.address) - await pNetworkMessageRelay.addNetwork("0x12345678") - await pNetworkMessageRelay.addNetwork("0x11223344") + const pNetworkMessageRelay = await PNetworkMessageRelay.deploy( + yaho.address, + DOMAIN_ID, + ZERO_ADDRESS, + pToken.address, + DOMAIN_ID, + ) await pToken.connect(wallet).send(pNetworkMessageRelay.address, 10000, "0x") const PNetworkAdapter = await ethers.getContractFactory("PNetworkAdapter") - const pNetworkAdapter = await PNetworkAdapter.deploy(vault.address, pNetworkMessageRelay.address) + const pNetworkAdapter = await PNetworkAdapter.deploy( + 1, + pNetworkMessageRelay.address, + vault.address, + pNetworkMessageRelay.address, + 1, + ) const PingPong = await ethers.getContractFactory("PingPong") const pingPong = await PingPong.deploy() const message_1 = { @@ -150,17 +154,9 @@ describe("PNetworkMessageRelayer", function () { it("Successfully deploys contract", async function () { const { pNetworkMessageRelay, pToken, yaho } = await setup() expect(await pNetworkMessageRelay.deployed()) - expect(await pNetworkMessageRelay.token()).to.equal(pToken.address) - expect(await pNetworkMessageRelay.vault()).to.equal(ZERO_ADDRESS) - expect(await pNetworkMessageRelay.yaho()).to.equal(yaho.address) - }) - - it("Should not permit to add again the same network", async function () { - const { pNetworkMessageRelay } = await setup() - await expect(pNetworkMessageRelay.addNetwork("0x11223344")).to.revertedWithCustomError( - pNetworkMessageRelay, - "AlreadyExistingNetworkId", - ) + expect(await pNetworkMessageRelay.TOKEN()).to.equal(pToken.address) + expect(await pNetworkMessageRelay.VAULT()).to.equal(ZERO_ADDRESS) + expect(await pNetworkMessageRelay.YAHO()).to.equal(yaho.address) }) }) @@ -169,10 +165,7 @@ describe("PNetworkMessageRelayer", function () { const { pNetworkMessageRelay, pNetworkAdapter, pToken, yaho } = await setup() const ids = [0, 1] const hashes = await Promise.all(ids.map(async (_id) => await yaho.hashes(_id))) - const expectedUserData = new ethers.utils.AbiCoder().encode( - ["uint256[]", "bytes32[]", "uint256"], - [ids, hashes, network.config.chainId], - ) + const expectedUserData = new ethers.utils.AbiCoder().encode(["uint256[]", "bytes32[]"], [ids, hashes]) await expect(pNetworkMessageRelay.relayMessages(ids, pNetworkAdapter.address)) .to.emit(pNetworkMessageRelay, "MessageRelayed") .withArgs(pNetworkMessageRelay.address, 0) @@ -183,16 +176,7 @@ describe("PNetworkMessageRelayer", function () { pNetworkAdapter.address.replace("0x", "").toLowerCase(), expectedUserData, "0x87654321", - "0x12345678", - ) - .and.to.emit(pToken, "Redeem") - .withArgs( - pNetworkMessageRelay.address, - 100, - pNetworkAdapter.address.replace("0x", "").toLowerCase(), - expectedUserData, - "0x87654321", - "0x11223344", + "0x005fe7f9", ) }) }) diff --git a/packages/evm/test/adapters/pnetwork/02_PNetworkAdapter.spec.ts b/packages/evm/test/adapters/pnetwork/02_PNetworkAdapter.spec.ts index e647d038..c0214506 100644 --- a/packages/evm/test/adapters/pnetwork/02_PNetworkAdapter.spec.ts +++ b/packages/evm/test/adapters/pnetwork/02_PNetworkAdapter.spec.ts @@ -3,7 +3,7 @@ import { ethers } from "hardhat" import { ZERO_ADDRESS, deployErc1820Registry, resetNetwork } from "./utils.spec" -const DOMAIN_ID = "0x0000000000000000000000000000000000000000000000000000000000007a69" +const DOMAIN_ID = "0x0000000000000000000000000000000000000000000000000000000000001" const ID_ONE = 1 const ID_TWO = 2 const HASH_ZERO = "0x0000000000000000000000000000000000000000000000000000000000000000" @@ -19,7 +19,13 @@ describe("PNetworkAdapter", function () { const PToken = await ethers.getContractFactory("PToken") const pToken = await PToken.deploy("pToken", "P", []) const PNetworkAdapter = await ethers.getContractFactory("PNetworkAdapter") - const pNetworkAdapter = await PNetworkAdapter.deploy(ZERO_ADDRESS, pToken.address) + const pNetworkAdapter = await PNetworkAdapter.deploy( + DOMAIN_ID, + ZERO_ADDRESS, + ZERO_ADDRESS, + pToken.address, + DOMAIN_ID, + ) return { wallet, pNetworkAdapter, @@ -30,20 +36,24 @@ describe("PNetworkAdapter", function () { describe("Constructor", function () { it("Successfully deploys contract with correct state", async function () { const { pNetworkAdapter, pToken } = await setup() + expect(await pNetworkAdapter.deployed()) - expect(await pNetworkAdapter.token()).to.equal(pToken.address) - expect(await pNetworkAdapter.admittedSender()).to.equal(ZERO_ADDRESS) + expect(await pNetworkAdapter.TOKEN()).to.equal(pToken.address) + expect(await pNetworkAdapter.ADMITTED_SENDER()).to.equal(ZERO_ADDRESS) }) }) describe("StoreHashes()", function () { - it("Stores hashes", async function () { + it("Should store hashes", async function () { const { pNetworkAdapter, wallet, pToken } = await setup() const coder = new ethers.utils.AbiCoder() const data = coder.encode( - ["uint256[]", "bytes32[]", "uint256"], - [[ID_ONE, ID_TWO], [HASH_ONE, HASH_TWO], DOMAIN_ID], + ["uint256[]", "bytes32[]"], + [ + [ID_ONE, ID_TWO], + [HASH_ONE, HASH_TWO], + ], ) await pToken.connect(wallet).mint(pNetworkAdapter.address, 1000, data, "0x") expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_ONE)).to.equal(HASH_ONE) @@ -58,8 +68,11 @@ describe("PNetworkAdapter", function () { const coder = new ethers.utils.AbiCoder() const data = coder.encode( - ["uint256[]", "bytes32[]", "uint256"], - [[ID_ONE, ID_TWO], [HASH_ONE, HASH_TWO], DOMAIN_ID], + ["uint256[]", "bytes32[]"], + [ + [ID_ONE, ID_TWO], + [HASH_ONE, HASH_TWO], + ], ) await expect(fakePToken.connect(wallet).mint(pNetworkAdapter.address, 1000, data, "0x")).to.be.revertedWith( "Only accepted token is allowed", @@ -73,8 +86,11 @@ describe("PNetworkAdapter", function () { const coder = new ethers.utils.AbiCoder() const data = coder.encode( - ["uint256[]", "bytes32[]", "uint256"], - [[ID_ONE, ID_TWO], [HASH_ONE, HASH_TWO], DOMAIN_ID], + ["uint256[]", "bytes32[]"], + [ + [ID_ONE, ID_TWO], + [HASH_ONE, HASH_TWO], + ], ) await expect(pToken.connect(wallet).send(pNetworkAdapter.address, 1000, data)) .to.be.revertedWithCustomError(pNetworkAdapter, "InvalidSender") @@ -83,29 +99,26 @@ describe("PNetworkAdapter", function () { expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_TWO)).to.equal(HASH_ZERO) }) - it("Should not store hashes when parallel arrays are mismatched", async function () { - const { pNetworkAdapter, wallet, pToken } = await setup() - - const coder = new ethers.utils.AbiCoder() - const data = coder.encode(["uint256[]", "bytes32[]", "uint256"], [[ID_ONE, ID_TWO], [HASH_ONE], DOMAIN_ID]) - await expect(pToken.connect(wallet).mint(pNetworkAdapter.address, 1000, data, "0x")) - .to.be.revertedWithCustomError(pNetworkAdapter, "ArrayLengthMismatch") - .withArgs() - expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_ONE)).to.equal(HASH_ZERO) - expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_TWO)).to.equal(HASH_ZERO) - }) - it("Overwrites previous hashes", async function () { const { pNetworkAdapter, wallet, pToken } = await setup() const coder = new ethers.utils.AbiCoder() let data = coder.encode( - ["uint256[]", "bytes32[]", "uint256"], - [[ID_ONE, ID_TWO], [HASH_ONE, HASH_TWO], DOMAIN_ID], + ["uint256[]", "bytes32[]"], + [ + [ID_ONE, ID_TWO], + [HASH_ONE, HASH_TWO], + ], ) await pToken.connect(wallet).mint(pNetworkAdapter.address, 1000, data, "0x") expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_ONE)).to.equal(HASH_ONE) expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_TWO)).to.equal(HASH_TWO) - data = coder.encode(["uint256[]", "bytes32[]", "uint256"], [[ID_TWO, ID_ONE], [HASH_ONE, HASH_TWO], DOMAIN_ID]) + data = coder.encode( + ["uint256[]", "bytes32[]"], + [ + [ID_TWO, ID_ONE], + [HASH_ONE, HASH_TWO], + ], + ) await pToken.connect(wallet).mint(pNetworkAdapter.address, 1000, data, "0x") expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_ONE)).to.equal(HASH_TWO) expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_TWO)).to.equal(HASH_ONE) @@ -129,8 +142,16 @@ describe("PNetworkAdapter", function () { const Vault = await ethers.getContractFactory("MockVault") const vault = await Vault.deploy() await vault.initialize([erc777Token.address, anotherErc777Token.address], "0x12345678") + const PNetworkAdapter = await ethers.getContractFactory("PNetworkAdapter") - const pNetworkAdapter = await PNetworkAdapter.deploy(vault.address, erc777Token.address) + const pNetworkAdapter = await PNetworkAdapter.deploy( + DOMAIN_ID, + ZERO_ADDRESS, + vault.address, + erc777Token.address, + DOMAIN_ID, + ) + const coder = new ethers.utils.AbiCoder() const data = coder.encode( ["bytes32", "string", "bytes4"], @@ -151,8 +172,8 @@ describe("PNetworkAdapter", function () { it("Successfully deploys contract with correct state", async function () { const { pNetworkAdapter, erc777Token, vault } = await setup() expect(await pNetworkAdapter.deployed()) - expect(await pNetworkAdapter.token()).to.equal(erc777Token.address) - expect(await pNetworkAdapter.admittedSender()).to.equal(vault.address) + expect(await pNetworkAdapter.TOKEN()).to.equal(erc777Token.address) + expect(await pNetworkAdapter.ADMITTED_SENDER()).to.equal(vault.address) }) }) @@ -162,8 +183,11 @@ describe("PNetworkAdapter", function () { const coder = new ethers.utils.AbiCoder() const data = coder.encode( - ["uint256[]", "bytes32[]", "uint256"], - [[ID_ONE, ID_TWO], [HASH_ONE, HASH_TWO], DOMAIN_ID], + ["uint256[]", "bytes32[]"], + [ + [ID_ONE, ID_TWO], + [HASH_ONE, HASH_TWO], + ], ) await vault.connect(wallet).pegOut(pNetworkAdapter.address, erc777Token.address, 100, data) expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_ONE)).to.equal(HASH_ONE) @@ -175,8 +199,11 @@ describe("PNetworkAdapter", function () { const coder = new ethers.utils.AbiCoder() const data = coder.encode( - ["uint256[]", "bytes32[]", "uint256"], - [[ID_ONE, ID_TWO], [HASH_ONE, HASH_TWO], DOMAIN_ID], + ["uint256[]", "bytes32[]"], + [ + [ID_ONE, ID_TWO], + [HASH_ONE, HASH_TWO], + ], ) await expect( vault.connect(wallet).pegOut(pNetworkAdapter.address, anotherErc777Token.address, 100, data), @@ -190,8 +217,11 @@ describe("PNetworkAdapter", function () { const coder = new ethers.utils.AbiCoder() const data = coder.encode( - ["uint256[]", "bytes32[]", "uint256"], - [[ID_ONE, ID_TWO], [HASH_ONE, HASH_TWO], DOMAIN_ID], + ["uint256[]", "bytes32[]"], + [ + [ID_ONE, ID_TWO], + [HASH_ONE, HASH_TWO], + ], ) await expect(erc777Token.connect(wallet).send(pNetworkAdapter.address, 100, data)) .to.be.revertedWithCustomError(pNetworkAdapter, "InvalidSender") @@ -200,28 +230,26 @@ describe("PNetworkAdapter", function () { expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_TWO)).to.equal(HASH_ZERO) }) - it("Should not store hashes when parallel arrays are mismatched", async function () { - const { pNetworkAdapter, wallet, erc777Token, vault } = await setup() - const coder = new ethers.utils.AbiCoder() - const data = coder.encode(["uint256[]", "bytes32[]", "uint256"], [[ID_ONE, ID_TWO], [HASH_ONE], DOMAIN_ID]) - await expect(vault.connect(wallet).pegOut(pNetworkAdapter.address, erc777Token.address, 100, data)) - .to.be.revertedWithCustomError(pNetworkAdapter, "ArrayLengthMismatch") - .withArgs() - expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_ONE)).to.equal(HASH_ZERO) - expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_TWO)).to.equal(HASH_ZERO) - }) - it("Overwrites previous hashes", async function () { const { pNetworkAdapter, wallet, erc777Token, vault } = await setup() const coder = new ethers.utils.AbiCoder() let data = coder.encode( - ["uint256[]", "bytes32[]", "uint256"], - [[ID_ONE, ID_TWO], [HASH_ONE, HASH_TWO], DOMAIN_ID], + ["uint256[]", "bytes32[]"], + [ + [ID_ONE, ID_TWO], + [HASH_ONE, HASH_TWO], + ], ) await vault.connect(wallet).pegOut(pNetworkAdapter.address, erc777Token.address, 50, data) expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_ONE)).to.equal(HASH_ONE) expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_TWO)).to.equal(HASH_TWO) - data = coder.encode(["uint256[]", "bytes32[]", "uint256"], [[ID_TWO, ID_ONE], [HASH_ONE, HASH_TWO], DOMAIN_ID]) + data = coder.encode( + ["uint256[]", "bytes32[]"], + [ + [ID_TWO, ID_ONE], + [HASH_ONE, HASH_TWO], + ], + ) await vault.connect(wallet).pegOut(pNetworkAdapter.address, erc777Token.address, 50, data) expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_ONE)).to.equal(HASH_TWO) expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_TWO)).to.equal(HASH_ONE) From 0f7d27b3718c1c4838974e8b28cd858a690c7608 Mon Sep 17 00:00:00 2001 From: Alain Olivier Date: Tue, 5 Dec 2023 17:44:13 +0100 Subject: [PATCH 05/19] fix(evm): revise metadata decoding in pNetwork adapter --- .../adapters/pnetwork/PNetworkAdapter.sol | 8 +++- .../pnetwork/02_PNetworkAdapter.spec.ts | 41 +++++++++++++------ 2 files changed, 36 insertions(+), 13 deletions(-) diff --git a/packages/evm/contracts/adapters/pnetwork/PNetworkAdapter.sol b/packages/evm/contracts/adapters/pnetwork/PNetworkAdapter.sol index bf8488c6..125cb908 100644 --- a/packages/evm/contracts/adapters/pnetwork/PNetworkAdapter.sol +++ b/packages/evm/contracts/adapters/pnetwork/PNetworkAdapter.sol @@ -35,6 +35,12 @@ contract PNetworkAdapter is HeaderOracleAdapter, PNetworkBase { ) external override { require(msg.sender == address(TOKEN), "Only accepted token is allowed"); if (from != ADMITTED_SENDER) revert InvalidSender(from); - _receivePayload(data); + (, bytes memory userData, bytes4 networkId, address sender) = abi.decode( + data, + (bytes1, bytes, bytes4, address) + ); + require(networkId == SUPPORTED_NETWORK_IDS[PNETWORK_REF_CHAIN], "Invalid source network ID"); + require(sender == REPORTER_ADDRESS, "Invalid reporter"); + _receivePayload(userData); } } diff --git a/packages/evm/test/adapters/pnetwork/02_PNetworkAdapter.spec.ts b/packages/evm/test/adapters/pnetwork/02_PNetworkAdapter.spec.ts index c0214506..7b34838a 100644 --- a/packages/evm/test/adapters/pnetwork/02_PNetworkAdapter.spec.ts +++ b/packages/evm/test/adapters/pnetwork/02_PNetworkAdapter.spec.ts @@ -3,6 +3,7 @@ import { ethers } from "hardhat" import { ZERO_ADDRESS, deployErc1820Registry, resetNetwork } from "./utils.spec" +const REPORTER_ADDRESS = "0xd5e099c71b797516c10ed0f0d895f429c2781142" const DOMAIN_ID = "0x0000000000000000000000000000000000000000000000000000000000001" const ID_ONE = 1 const ID_TWO = 2 @@ -10,6 +11,12 @@ const HASH_ZERO = "0x00000000000000000000000000000000000000000000000000000000000 const HASH_ONE = "0x0000000000000000000000000000000000000000000000000000000000000001" const HASH_TWO = "0x0000000000000000000000000000000000000000000000000000000000000002" +const encodeToMetadata = (userData: string, originChainId: string, sender: string, version = 3) => + new ethers.utils.AbiCoder().encode( + ["bytes1", "bytes", "bytes4", "address"], + [version, userData, originChainId, sender], + ) + describe("PNetworkAdapter", function () { describe("Host Blockchain", () => { const setup = async () => { @@ -21,7 +28,7 @@ describe("PNetworkAdapter", function () { const PNetworkAdapter = await ethers.getContractFactory("PNetworkAdapter") const pNetworkAdapter = await PNetworkAdapter.deploy( DOMAIN_ID, - ZERO_ADDRESS, + REPORTER_ADDRESS, ZERO_ADDRESS, pToken.address, DOMAIN_ID, @@ -48,13 +55,14 @@ describe("PNetworkAdapter", function () { const { pNetworkAdapter, wallet, pToken } = await setup() const coder = new ethers.utils.AbiCoder() - const data = coder.encode( + const userData = coder.encode( ["uint256[]", "bytes32[]"], [ [ID_ONE, ID_TWO], [HASH_ONE, HASH_TWO], ], ) + const data = encodeToMetadata(userData, "0x005fe7f9", REPORTER_ADDRESS) await pToken.connect(wallet).mint(pNetworkAdapter.address, 1000, data, "0x") expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_ONE)).to.equal(HASH_ONE) expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_TWO)).to.equal(HASH_TWO) @@ -67,13 +75,14 @@ describe("PNetworkAdapter", function () { const fakePToken = await PToken.deploy("pToken", "P", []) const coder = new ethers.utils.AbiCoder() - const data = coder.encode( + const userData = coder.encode( ["uint256[]", "bytes32[]"], [ [ID_ONE, ID_TWO], [HASH_ONE, HASH_TWO], ], ) + const data = encodeToMetadata(userData, "0x005fe7f9", REPORTER_ADDRESS) await expect(fakePToken.connect(wallet).mint(pNetworkAdapter.address, 1000, data, "0x")).to.be.revertedWith( "Only accepted token is allowed", ) @@ -85,13 +94,14 @@ describe("PNetworkAdapter", function () { const { pNetworkAdapter, wallet, pToken } = await setup() const coder = new ethers.utils.AbiCoder() - const data = coder.encode( + const userData = coder.encode( ["uint256[]", "bytes32[]"], [ [ID_ONE, ID_TWO], [HASH_ONE, HASH_TWO], ], ) + const data = encodeToMetadata(userData, "0x005fe7f9", REPORTER_ADDRESS) await expect(pToken.connect(wallet).send(pNetworkAdapter.address, 1000, data)) .to.be.revertedWithCustomError(pNetworkAdapter, "InvalidSender") .withArgs(wallet.address) @@ -102,23 +112,25 @@ describe("PNetworkAdapter", function () { it("Overwrites previous hashes", async function () { const { pNetworkAdapter, wallet, pToken } = await setup() const coder = new ethers.utils.AbiCoder() - let data = coder.encode( + let userData = coder.encode( ["uint256[]", "bytes32[]"], [ [ID_ONE, ID_TWO], [HASH_ONE, HASH_TWO], ], ) + let data = encodeToMetadata(userData, "0x005fe7f9", REPORTER_ADDRESS) await pToken.connect(wallet).mint(pNetworkAdapter.address, 1000, data, "0x") expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_ONE)).to.equal(HASH_ONE) expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_TWO)).to.equal(HASH_TWO) - data = coder.encode( + userData = coder.encode( ["uint256[]", "bytes32[]"], [ [ID_TWO, ID_ONE], [HASH_ONE, HASH_TWO], ], ) + data = encodeToMetadata(userData, "0x005fe7f9", REPORTER_ADDRESS) await pToken.connect(wallet).mint(pNetworkAdapter.address, 1000, data, "0x") expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_ONE)).to.equal(HASH_TWO) expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_TWO)).to.equal(HASH_ONE) @@ -146,7 +158,7 @@ describe("PNetworkAdapter", function () { const PNetworkAdapter = await ethers.getContractFactory("PNetworkAdapter") const pNetworkAdapter = await PNetworkAdapter.deploy( DOMAIN_ID, - ZERO_ADDRESS, + REPORTER_ADDRESS, vault.address, erc777Token.address, DOMAIN_ID, @@ -182,13 +194,14 @@ describe("PNetworkAdapter", function () { const { pNetworkAdapter, wallet, vault, erc777Token } = await setup() const coder = new ethers.utils.AbiCoder() - const data = coder.encode( + const userData = coder.encode( ["uint256[]", "bytes32[]"], [ [ID_ONE, ID_TWO], [HASH_ONE, HASH_TWO], ], ) + const data = encodeToMetadata(userData, "0x005fe7f9", REPORTER_ADDRESS) await vault.connect(wallet).pegOut(pNetworkAdapter.address, erc777Token.address, 100, data) expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_ONE)).to.equal(HASH_ONE) expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_TWO)).to.equal(HASH_TWO) @@ -198,13 +211,14 @@ describe("PNetworkAdapter", function () { const { pNetworkAdapter, wallet, vault, anotherErc777Token } = await setup() const coder = new ethers.utils.AbiCoder() - const data = coder.encode( + const userData = coder.encode( ["uint256[]", "bytes32[]"], [ [ID_ONE, ID_TWO], [HASH_ONE, HASH_TWO], ], ) + const data = encodeToMetadata(userData, "0x005fe7f9", REPORTER_ADDRESS) await expect( vault.connect(wallet).pegOut(pNetworkAdapter.address, anotherErc777Token.address, 100, data), ).to.be.revertedWith("Only accepted token is allowed") @@ -216,13 +230,14 @@ describe("PNetworkAdapter", function () { const { pNetworkAdapter, wallet, erc777Token } = await setup() const coder = new ethers.utils.AbiCoder() - const data = coder.encode( + const userData = coder.encode( ["uint256[]", "bytes32[]"], [ [ID_ONE, ID_TWO], [HASH_ONE, HASH_TWO], ], ) + const data = encodeToMetadata(userData, "0x005fe7f9", REPORTER_ADDRESS) await expect(erc777Token.connect(wallet).send(pNetworkAdapter.address, 100, data)) .to.be.revertedWithCustomError(pNetworkAdapter, "InvalidSender") .withArgs(wallet.address) @@ -233,23 +248,25 @@ describe("PNetworkAdapter", function () { it("Overwrites previous hashes", async function () { const { pNetworkAdapter, wallet, erc777Token, vault } = await setup() const coder = new ethers.utils.AbiCoder() - let data = coder.encode( + let userData = coder.encode( ["uint256[]", "bytes32[]"], [ [ID_ONE, ID_TWO], [HASH_ONE, HASH_TWO], ], ) + let data = encodeToMetadata(userData, "0x005fe7f9", REPORTER_ADDRESS) await vault.connect(wallet).pegOut(pNetworkAdapter.address, erc777Token.address, 50, data) expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_ONE)).to.equal(HASH_ONE) expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_TWO)).to.equal(HASH_TWO) - data = coder.encode( + userData = coder.encode( ["uint256[]", "bytes32[]"], [ [ID_TWO, ID_ONE], [HASH_ONE, HASH_TWO], ], ) + data = encodeToMetadata(userData, "0x005fe7f9", REPORTER_ADDRESS) await vault.connect(wallet).pegOut(pNetworkAdapter.address, erc777Token.address, 50, data) expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_ONE)).to.equal(HASH_TWO) expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_TWO)).to.equal(HASH_ONE) From 62ada1c313eafc2181848a5f035a71a183913ce6 Mon Sep 17 00:00:00 2001 From: Alain Olivier Date: Tue, 5 Dec 2023 18:43:40 +0100 Subject: [PATCH 06/19] refactor(evm): remove unnecessary argument from pNetwork componenents' constructor --- .../contracts/adapters/pnetwork/PNetworkAdapter.sol | 5 ++--- .../adapters/pnetwork/PNetworkHeaderReporter.sol | 5 ++--- .../adapters/pnetwork/PNetworkMessageRelayer.sol | 5 ++--- .../contracts/adapters/pnetwork/PNetworkReporter.sol | 2 +- packages/evm/tasks/deploy/adapters/pnetwork.ts | 11 ++++------- .../adapters/pnetwork/01_PNetworkMessageRelay.spec.ts | 8 ++------ .../test/adapters/pnetwork/02_PNetworkAdapter.spec.ts | 9 +-------- 7 files changed, 14 insertions(+), 31 deletions(-) diff --git a/packages/evm/contracts/adapters/pnetwork/PNetworkAdapter.sol b/packages/evm/contracts/adapters/pnetwork/PNetworkAdapter.sol index 125cb908..ed2d0240 100644 --- a/packages/evm/contracts/adapters/pnetwork/PNetworkAdapter.sol +++ b/packages/evm/contracts/adapters/pnetwork/PNetworkAdapter.sol @@ -17,9 +17,8 @@ contract PNetworkAdapter is HeaderOracleAdapter, PNetworkBase { uint256 reporterChain, address reporterAddress, address pNetworkAdmittedsender, - address pNetworkToken, - uint32 pNetworkReporterChain - ) HeaderOracleAdapter(reporterChain, reporterAddress) PNetworkBase(pNetworkReporterChain) { + address pNetworkToken + ) HeaderOracleAdapter(reporterChain, reporterAddress) PNetworkBase(reporterChain) { ADMITTED_SENDER = pNetworkAdmittedsender; TOKEN = pNetworkToken; } diff --git a/packages/evm/contracts/adapters/pnetwork/PNetworkHeaderReporter.sol b/packages/evm/contracts/adapters/pnetwork/PNetworkHeaderReporter.sol index 9e923a0d..62fd2d16 100644 --- a/packages/evm/contracts/adapters/pnetwork/PNetworkHeaderReporter.sol +++ b/packages/evm/contracts/adapters/pnetwork/PNetworkHeaderReporter.sol @@ -9,9 +9,8 @@ contract PNetworkHeaderReporter is HeaderReporter, PNetworkReporter { address headerStorage, uint64 adapterChain, address vault, - address token, - uint32 pNetworkAdapterChain - ) HeaderReporter(headerStorage, adapterChain) PNetworkReporter(vault, token, pNetworkAdapterChain) {} // solhint-disable no-empty-blocks + address token + ) HeaderReporter(headerStorage, adapterChain) PNetworkReporter(vault, token, adapterChain) {} // solhint-disable no-empty-blocks function _sendPayload(bytes memory payload, address adapter) internal override { _pNetworkSend(payload, adapter); diff --git a/packages/evm/contracts/adapters/pnetwork/PNetworkMessageRelayer.sol b/packages/evm/contracts/adapters/pnetwork/PNetworkMessageRelayer.sol index 444c0522..ed8bcdd7 100644 --- a/packages/evm/contracts/adapters/pnetwork/PNetworkMessageRelayer.sol +++ b/packages/evm/contracts/adapters/pnetwork/PNetworkMessageRelayer.sol @@ -9,9 +9,8 @@ contract PNetworkMessageRelay is MessageRelay, PNetworkReporter { address yaho, uint64 adapterChain, address vault, - address token, - uint32 pNetworkAdapterChain - ) MessageRelay(yaho, adapterChain) PNetworkReporter(vault, token, pNetworkAdapterChain) {} // solhint-disable no-empty-blocks + address token + ) MessageRelay(yaho, adapterChain) PNetworkReporter(vault, token, adapterChain) {} // solhint-disable no-empty-blocks function _sendPayload(bytes memory payload, address adapter) internal override { _pNetworkSend(payload, adapter); diff --git a/packages/evm/contracts/adapters/pnetwork/PNetworkReporter.sol b/packages/evm/contracts/adapters/pnetwork/PNetworkReporter.sol index 8b43cad5..d71df3df 100644 --- a/packages/evm/contracts/adapters/pnetwork/PNetworkReporter.sol +++ b/packages/evm/contracts/adapters/pnetwork/PNetworkReporter.sol @@ -13,7 +13,7 @@ abstract contract PNetworkReporter is PNetworkBase { address public immutable VAULT; address public immutable TOKEN; - constructor(address vault, address token, uint256 pNetworkAdapterChain) PNetworkBase(pNetworkAdapterChain) { + constructor(address vault, address token, uint256 adapterChain) PNetworkBase(adapterChain) { VAULT = vault; TOKEN = token; } diff --git a/packages/evm/tasks/deploy/adapters/pnetwork.ts b/packages/evm/tasks/deploy/adapters/pnetwork.ts index 7b4cd9fe..a0ff0427 100644 --- a/packages/evm/tasks/deploy/adapters/pnetwork.ts +++ b/packages/evm/tasks/deploy/adapters/pnetwork.ts @@ -11,8 +11,8 @@ import type { PNetworkMessageRelay__factory } from "../../../types/factories/con import { verify } from "../index" task("deploy:adapter:PNetworkHeaderReporter") - .addParam("chainId", "chain id of the adapter contract") .addParam("yaho", "address of the Yaho contract") + .addParam("chainId", "chain id of the adapter contract") .addParam("vault", "address of the vault contract (address 0 when deploying on non-native chain)") .addParam("token", "address of the token used to transfer data") .addFlag("verify", "whether to verify the contract on Etherscan") @@ -27,7 +27,6 @@ task("deploy:adapter:PNetworkHeaderReporter") taskArguments.chainId, taskArguments.vault, taskArguments.token, - taskArguments.chainId, ] as const const pNetworkHeaderReporter: PNetworkHeaderReporter = ( await pNetworkHeaderReporterFactory.connect(signers[0]).deploy(...constructorArguments) @@ -39,8 +38,8 @@ task("deploy:adapter:PNetworkHeaderReporter") }) task("deploy:adapter:PNetworkMessageRelay") - .addParam("chainId", "chain id of the adapter contract") .addParam("yaho", "address of the Yaho contract") + .addParam("chainId", "chain id of the adapter contract") .addParam("vault", "address of the vault contract (address 0 when deploying on non-native chain)") .addParam("token", "address of the token used to transfer data") .addFlag("verify", "whether to verify the contract on Etherscan") @@ -55,7 +54,6 @@ task("deploy:adapter:PNetworkMessageRelay") taskArguments.chainId, taskArguments.vault, taskArguments.token, - taskArguments.chainId, ] as const const pNetworkMessageRelay: PNetworkMessageRelay = ( await pNetworkAdapterFactory.connect(signers[0]).deploy(...constructorArguments) @@ -79,11 +77,10 @@ task("deploy:adapter:PNetworkAdapter") await hre.ethers.getContractFactory("TelepathyAdapter") ) const constructorArguments = [ - taskArguments.chainIdm, + taskArguments.chainId, taskArguments.reporter, - taskArguments.vaultm, + taskArguments.vault, taskArguments.token, - taskArguments.chainId, ] as const const pNetworkAdapter: PNetworkAdapter = ( await pNetworkAdapterFactory.connect(signers[0]).deploy(...constructorArguments) diff --git a/packages/evm/test/adapters/pnetwork/01_PNetworkMessageRelay.spec.ts b/packages/evm/test/adapters/pnetwork/01_PNetworkMessageRelay.spec.ts index d6fae458..8e1b1294 100644 --- a/packages/evm/test/adapters/pnetwork/01_PNetworkMessageRelay.spec.ts +++ b/packages/evm/test/adapters/pnetwork/01_PNetworkMessageRelay.spec.ts @@ -26,18 +26,16 @@ describe("PNetworkMessageRelayer", function () { DOMAIN_ID, vault.address, erc777Token.address, - DOMAIN_ID, ) await erc777Token.connect(wallet).send(pNetworkMessageRelay.address, 10000, "0x") const PNetworkAdapter = await ethers.getContractFactory("PNetworkAdapter") const pNetworkAdapter = await PNetworkAdapter.deploy( - 1, + DOMAIN_ID, pNetworkMessageRelay.address, vault.address, pNetworkMessageRelay.address, - 1, ) const PingPong = await ethers.getContractFactory("PingPong") @@ -116,18 +114,16 @@ describe("PNetworkMessageRelayer", function () { DOMAIN_ID, ZERO_ADDRESS, pToken.address, - DOMAIN_ID, ) await pToken.connect(wallet).send(pNetworkMessageRelay.address, 10000, "0x") const PNetworkAdapter = await ethers.getContractFactory("PNetworkAdapter") const pNetworkAdapter = await PNetworkAdapter.deploy( - 1, + DOMAIN_ID, pNetworkMessageRelay.address, vault.address, pNetworkMessageRelay.address, - 1, ) const PingPong = await ethers.getContractFactory("PingPong") const pingPong = await PingPong.deploy() diff --git a/packages/evm/test/adapters/pnetwork/02_PNetworkAdapter.spec.ts b/packages/evm/test/adapters/pnetwork/02_PNetworkAdapter.spec.ts index 7b34838a..b4d3a46f 100644 --- a/packages/evm/test/adapters/pnetwork/02_PNetworkAdapter.spec.ts +++ b/packages/evm/test/adapters/pnetwork/02_PNetworkAdapter.spec.ts @@ -26,13 +26,7 @@ describe("PNetworkAdapter", function () { const PToken = await ethers.getContractFactory("PToken") const pToken = await PToken.deploy("pToken", "P", []) const PNetworkAdapter = await ethers.getContractFactory("PNetworkAdapter") - const pNetworkAdapter = await PNetworkAdapter.deploy( - DOMAIN_ID, - REPORTER_ADDRESS, - ZERO_ADDRESS, - pToken.address, - DOMAIN_ID, - ) + const pNetworkAdapter = await PNetworkAdapter.deploy(DOMAIN_ID, REPORTER_ADDRESS, ZERO_ADDRESS, pToken.address) return { wallet, pNetworkAdapter, @@ -161,7 +155,6 @@ describe("PNetworkAdapter", function () { REPORTER_ADDRESS, vault.address, erc777Token.address, - DOMAIN_ID, ) const coder = new ethers.utils.AbiCoder() From af64f4f2c1b14930b3187addd4ebd7fd2ff81f27 Mon Sep 17 00:00:00 2001 From: Alain Olivier Date: Tue, 5 Dec 2023 19:17:04 +0100 Subject: [PATCH 07/19] test(evm): add further test for pNetwork adapter --- .../pnetwork/02_PNetworkAdapter.spec.ts | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/packages/evm/test/adapters/pnetwork/02_PNetworkAdapter.spec.ts b/packages/evm/test/adapters/pnetwork/02_PNetworkAdapter.spec.ts index b4d3a46f..477d0f13 100644 --- a/packages/evm/test/adapters/pnetwork/02_PNetworkAdapter.spec.ts +++ b/packages/evm/test/adapters/pnetwork/02_PNetworkAdapter.spec.ts @@ -238,6 +238,45 @@ describe("PNetworkAdapter", function () { expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_TWO)).to.equal(HASH_ZERO) }) + it("Should not store hashes when data is received from another chain", async function () { + const { pNetworkAdapter, wallet, vault, erc777Token } = await setup() + + const coder = new ethers.utils.AbiCoder() + const userData = coder.encode( + ["uint256[]", "bytes32[]"], + [ + [ID_ONE, ID_TWO], + [HASH_ONE, HASH_TWO], + ], + ) + const data = encodeToMetadata(userData, "0x00e4b170", REPORTER_ADDRESS) + await expect( + vault.connect(wallet).pegOut(pNetworkAdapter.address, erc777Token.address, 100, data), + ).to.be.revertedWith("Invalid source network ID") + expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_ONE)).to.equal(HASH_ZERO) + expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_TWO)).to.equal(HASH_ZERO) + }) + + it("Should not store hashes when data originated from another address", async function () { + const { pNetworkAdapter, wallet, vault, erc777Token } = await setup() + const WRONG_REPORTER_ADDRESS = "0xa5e099c71b797516c10ed0f0d895f429c2781142" + + const coder = new ethers.utils.AbiCoder() + const userData = coder.encode( + ["uint256[]", "bytes32[]"], + [ + [ID_ONE, ID_TWO], + [HASH_ONE, HASH_TWO], + ], + ) + const data = encodeToMetadata(userData, "0x005fe7f9", WRONG_REPORTER_ADDRESS) + await expect( + vault.connect(wallet).pegOut(pNetworkAdapter.address, erc777Token.address, 100, data), + ).to.be.revertedWith("Invalid reporter") + expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_ONE)).to.equal(HASH_ZERO) + expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_TWO)).to.equal(HASH_ZERO) + }) + it("Overwrites previous hashes", async function () { const { pNetworkAdapter, wallet, erc777Token, vault } = await setup() const coder = new ethers.utils.AbiCoder() From 2f0969b7402bd2cb3c0c98e7f6a667cc02bd3b44 Mon Sep 17 00:00:00 2001 From: Alain Olivier Date: Mon, 11 Dec 2023 14:09:26 +0100 Subject: [PATCH 08/19] refactor(evm): revise constructor arguments for pNetwork contracts --- .../adapters/pnetwork/PNetworkAdapter.sol | 7 ++++--- .../adapters/pnetwork/PNetworkBase.sol | 13 +++---------- .../pnetwork/PNetworkHeaderReporter.sol | 5 +++-- .../pnetwork/PNetworkMessageRelayer.sol | 5 +++-- .../adapters/pnetwork/PNetworkReporter.sol | 17 +++-------------- .../pnetwork/01_PNetworkMessageRelay.spec.ts | 5 +++++ .../pnetwork/02_PNetworkAdapter.spec.ts | 9 ++++++++- 7 files changed, 29 insertions(+), 32 deletions(-) diff --git a/packages/evm/contracts/adapters/pnetwork/PNetworkAdapter.sol b/packages/evm/contracts/adapters/pnetwork/PNetworkAdapter.sol index ed2d0240..0b05a59f 100644 --- a/packages/evm/contracts/adapters/pnetwork/PNetworkAdapter.sol +++ b/packages/evm/contracts/adapters/pnetwork/PNetworkAdapter.sol @@ -17,8 +17,9 @@ contract PNetworkAdapter is HeaderOracleAdapter, PNetworkBase { uint256 reporterChain, address reporterAddress, address pNetworkAdmittedsender, - address pNetworkToken - ) HeaderOracleAdapter(reporterChain, reporterAddress) PNetworkBase(reporterChain) { + address pNetworkToken, + bytes4 pNetworkSourceNetworkId + ) HeaderOracleAdapter(reporterChain, reporterAddress) PNetworkBase(pNetworkSourceNetworkId) { ADMITTED_SENDER = pNetworkAdmittedsender; TOKEN = pNetworkToken; } @@ -38,7 +39,7 @@ contract PNetworkAdapter is HeaderOracleAdapter, PNetworkBase { data, (bytes1, bytes, bytes4, address) ); - require(networkId == SUPPORTED_NETWORK_IDS[PNETWORK_REF_CHAIN], "Invalid source network ID"); + require(networkId == PNETWORK_REF_NETWORK_ID, "Invalid source network ID"); require(sender == REPORTER_ADDRESS, "Invalid reporter"); _receivePayload(userData); } diff --git a/packages/evm/contracts/adapters/pnetwork/PNetworkBase.sol b/packages/evm/contracts/adapters/pnetwork/PNetworkBase.sol index 4da75770..8a0bdf6d 100644 --- a/packages/evm/contracts/adapters/pnetwork/PNetworkBase.sol +++ b/packages/evm/contracts/adapters/pnetwork/PNetworkBase.sol @@ -5,20 +5,13 @@ import "@openzeppelin/contracts/interfaces/IERC777Recipient.sol"; import "@openzeppelin/contracts-upgradeable/interfaces/IERC1820RegistryUpgradeable.sol"; abstract contract PNetworkBase is IERC777Recipient { - mapping(uint256 => bytes4) public SUPPORTED_NETWORK_IDS; - uint256 public immutable PNETWORK_REF_CHAIN; + bytes4 public immutable PNETWORK_REF_NETWORK_ID; IERC1820RegistryUpgradeable private constant ERC1820 = IERC1820RegistryUpgradeable(0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24); bytes32 private constant TOKENS_RECIPIENT_INTERFACE_HASH = keccak256("ERC777TokensRecipient"); - constructor(uint256 pNetworkRefChain) { - SUPPORTED_NETWORK_IDS[1] = 0x005fe7f9; // EthereumMainnet - SUPPORTED_NETWORK_IDS[64] = 0x00e4b170; // BscMainnet - SUPPORTED_NETWORK_IDS[100] = 0x00f1918e; // GnosisMainnet - SUPPORTED_NETWORK_IDS[137] = 0x0075dd4c; // PolygonMainnet - SUPPORTED_NETWORK_IDS[42161] = 0x00ce98c4; // ArbitrumMainnet - require(SUPPORTED_NETWORK_IDS[pNetworkRefChain] != 0, "Not supported chain"); - PNETWORK_REF_CHAIN = pNetworkRefChain; + constructor(bytes4 pNetworkRefNetworkId) { + PNETWORK_REF_NETWORK_ID = pNetworkRefNetworkId; ERC1820.setInterfaceImplementer(address(this), TOKENS_RECIPIENT_INTERFACE_HASH, address(this)); } } diff --git a/packages/evm/contracts/adapters/pnetwork/PNetworkHeaderReporter.sol b/packages/evm/contracts/adapters/pnetwork/PNetworkHeaderReporter.sol index 62fd2d16..a8af946d 100644 --- a/packages/evm/contracts/adapters/pnetwork/PNetworkHeaderReporter.sol +++ b/packages/evm/contracts/adapters/pnetwork/PNetworkHeaderReporter.sol @@ -9,8 +9,9 @@ contract PNetworkHeaderReporter is HeaderReporter, PNetworkReporter { address headerStorage, uint64 adapterChain, address vault, - address token - ) HeaderReporter(headerStorage, adapterChain) PNetworkReporter(vault, token, adapterChain) {} // solhint-disable no-empty-blocks + address token, + bytes4 pNetworkAdapterNetworkId + ) HeaderReporter(headerStorage, adapterChain) PNetworkReporter(vault, token, pNetworkAdapterNetworkId) {} // solhint-disable no-empty-blocks function _sendPayload(bytes memory payload, address adapter) internal override { _pNetworkSend(payload, adapter); diff --git a/packages/evm/contracts/adapters/pnetwork/PNetworkMessageRelayer.sol b/packages/evm/contracts/adapters/pnetwork/PNetworkMessageRelayer.sol index ed8bcdd7..b68ebf09 100644 --- a/packages/evm/contracts/adapters/pnetwork/PNetworkMessageRelayer.sol +++ b/packages/evm/contracts/adapters/pnetwork/PNetworkMessageRelayer.sol @@ -9,8 +9,9 @@ contract PNetworkMessageRelay is MessageRelay, PNetworkReporter { address yaho, uint64 adapterChain, address vault, - address token - ) MessageRelay(yaho, adapterChain) PNetworkReporter(vault, token, adapterChain) {} // solhint-disable no-empty-blocks + address token, + bytes4 pNetworkAdapterNetworkId + ) MessageRelay(yaho, adapterChain) PNetworkReporter(vault, token, pNetworkAdapterNetworkId) {} // solhint-disable no-empty-blocks function _sendPayload(bytes memory payload, address adapter) internal override { _pNetworkSend(payload, adapter); diff --git a/packages/evm/contracts/adapters/pnetwork/PNetworkReporter.sol b/packages/evm/contracts/adapters/pnetwork/PNetworkReporter.sol index d71df3df..bb9e2a97 100644 --- a/packages/evm/contracts/adapters/pnetwork/PNetworkReporter.sol +++ b/packages/evm/contracts/adapters/pnetwork/PNetworkReporter.sol @@ -13,7 +13,7 @@ abstract contract PNetworkReporter is PNetworkBase { address public immutable VAULT; address public immutable TOKEN; - constructor(address vault, address token, uint256 adapterChain) PNetworkBase(adapterChain) { + constructor(address vault, address token, bytes4 pNetworkAdapterNetworkId) PNetworkBase(pNetworkAdapterNetworkId) { VAULT = vault; TOKEN = token; } @@ -43,20 +43,9 @@ abstract contract PNetworkReporter is PNetworkBase { function _pNetworkSend(bytes memory payload, address adapter) internal { if (VAULT != address(0)) { IERC20(TOKEN).approve(VAULT, SWAP_AMOUNT); - IErc20Vault(VAULT).pegIn( - SWAP_AMOUNT, - TOKEN, - _toAsciiString(adapter), - payload, - SUPPORTED_NETWORK_IDS[PNETWORK_REF_CHAIN] - ); + IErc20Vault(VAULT).pegIn(SWAP_AMOUNT, TOKEN, _toAsciiString(adapter), payload, PNETWORK_REF_NETWORK_ID); } else { - IPToken(TOKEN).redeem( - SWAP_AMOUNT, - payload, - _toAsciiString(adapter), - SUPPORTED_NETWORK_IDS[PNETWORK_REF_CHAIN] - ); + IPToken(TOKEN).redeem(SWAP_AMOUNT, payload, _toAsciiString(adapter), PNETWORK_REF_NETWORK_ID); } } diff --git a/packages/evm/test/adapters/pnetwork/01_PNetworkMessageRelay.spec.ts b/packages/evm/test/adapters/pnetwork/01_PNetworkMessageRelay.spec.ts index 8e1b1294..dd759960 100644 --- a/packages/evm/test/adapters/pnetwork/01_PNetworkMessageRelay.spec.ts +++ b/packages/evm/test/adapters/pnetwork/01_PNetworkMessageRelay.spec.ts @@ -26,6 +26,7 @@ describe("PNetworkMessageRelayer", function () { DOMAIN_ID, vault.address, erc777Token.address, + "0x005fe7f9", ) await erc777Token.connect(wallet).send(pNetworkMessageRelay.address, 10000, "0x") @@ -36,6 +37,7 @@ describe("PNetworkMessageRelayer", function () { pNetworkMessageRelay.address, vault.address, pNetworkMessageRelay.address, + "0x005fe7f9", ) const PingPong = await ethers.getContractFactory("PingPong") @@ -114,6 +116,7 @@ describe("PNetworkMessageRelayer", function () { DOMAIN_ID, ZERO_ADDRESS, pToken.address, + "0x005fe7f9", ) await pToken.connect(wallet).send(pNetworkMessageRelay.address, 10000, "0x") @@ -124,7 +127,9 @@ describe("PNetworkMessageRelayer", function () { pNetworkMessageRelay.address, vault.address, pNetworkMessageRelay.address, + "0x005fe7f9", ) + const PingPong = await ethers.getContractFactory("PingPong") const pingPong = await PingPong.deploy() const message_1 = { diff --git a/packages/evm/test/adapters/pnetwork/02_PNetworkAdapter.spec.ts b/packages/evm/test/adapters/pnetwork/02_PNetworkAdapter.spec.ts index 477d0f13..a68b08a7 100644 --- a/packages/evm/test/adapters/pnetwork/02_PNetworkAdapter.spec.ts +++ b/packages/evm/test/adapters/pnetwork/02_PNetworkAdapter.spec.ts @@ -26,7 +26,13 @@ describe("PNetworkAdapter", function () { const PToken = await ethers.getContractFactory("PToken") const pToken = await PToken.deploy("pToken", "P", []) const PNetworkAdapter = await ethers.getContractFactory("PNetworkAdapter") - const pNetworkAdapter = await PNetworkAdapter.deploy(DOMAIN_ID, REPORTER_ADDRESS, ZERO_ADDRESS, pToken.address) + const pNetworkAdapter = await PNetworkAdapter.deploy( + DOMAIN_ID, + REPORTER_ADDRESS, + ZERO_ADDRESS, + pToken.address, + "0x005fe7f9", + ) return { wallet, pNetworkAdapter, @@ -155,6 +161,7 @@ describe("PNetworkAdapter", function () { REPORTER_ADDRESS, vault.address, erc777Token.address, + "0x005fe7f9", ) const coder = new ethers.utils.AbiCoder() From 1189a91ed8c9774daeaa063fd01ddf56ff535636 Mon Sep 17 00:00:00 2001 From: Alain Olivier Date: Mon, 11 Dec 2023 15:04:39 +0100 Subject: [PATCH 09/19] refactor(evm): make use of custom errors in pNetwork contracts --- .../contracts/adapters/pnetwork/Errors.sol | 11 ++++++ .../adapters/pnetwork/PNetworkAdapter.sol | 26 +++++-------- .../adapters/pnetwork/PNetworkBase.sol | 25 +++++++++++- .../adapters/pnetwork/PNetworkReporter.sol | 29 +++----------- .../pnetwork/02_PNetworkAdapter.spec.ts | 38 +++++++++---------- 5 files changed, 69 insertions(+), 60 deletions(-) create mode 100644 packages/evm/contracts/adapters/pnetwork/Errors.sol diff --git a/packages/evm/contracts/adapters/pnetwork/Errors.sol b/packages/evm/contracts/adapters/pnetwork/Errors.sol new file mode 100644 index 00000000..84e3f847 --- /dev/null +++ b/packages/evm/contracts/adapters/pnetwork/Errors.sol @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: LGPL-3.0-only +pragma solidity ^0.8.17; + +library Errors { + error ArrayLengthMismatch(); + error InvalidToken(address token, address expected); + error InvalidSender(address sender, address expected); + error InvalidReceiver(address receiver, address expected); + error InvalidNetworkId(bytes4 networkId, bytes4 expected); + error UnauthorizedPNetworkReceive(); +} diff --git a/packages/evm/contracts/adapters/pnetwork/PNetworkAdapter.sol b/packages/evm/contracts/adapters/pnetwork/PNetworkAdapter.sol index 0b05a59f..38ce06f7 100644 --- a/packages/evm/contracts/adapters/pnetwork/PNetworkAdapter.sol +++ b/packages/evm/contracts/adapters/pnetwork/PNetworkAdapter.sol @@ -5,24 +5,19 @@ import "@openzeppelin/contracts/token/ERC777/presets/ERC777PresetFixedSupply.sol import { HeaderOracleAdapter } from "../HeaderOracleAdapter.sol"; import { PNetworkBase } from "./PNetworkBase.sol"; +import { Errors } from "./Errors.sol"; contract PNetworkAdapter is HeaderOracleAdapter, PNetworkBase { - address public immutable ADMITTED_SENDER; - address public immutable TOKEN; - - error ArrayLengthMismatch(); - error InvalidSender(address sender); - constructor( uint256 reporterChain, address reporterAddress, - address pNetworkAdmittedsender, + address pNetworkVault, address pNetworkToken, bytes4 pNetworkSourceNetworkId - ) HeaderOracleAdapter(reporterChain, reporterAddress) PNetworkBase(pNetworkSourceNetworkId) { - ADMITTED_SENDER = pNetworkAdmittedsender; - TOKEN = pNetworkToken; - } + ) + HeaderOracleAdapter(reporterChain, reporterAddress) + PNetworkBase(pNetworkVault, pNetworkToken, pNetworkSourceNetworkId) + {} // Implement the ERC777TokensRecipient interface function tokensReceived( @@ -32,15 +27,14 @@ contract PNetworkAdapter is HeaderOracleAdapter, PNetworkBase { uint256, bytes calldata data, bytes calldata - ) external override { - require(msg.sender == address(TOKEN), "Only accepted token is allowed"); - if (from != ADMITTED_SENDER) revert InvalidSender(from); + ) external override onlySupportedToken(msg.sender) { + if (from != VAULT) revert Errors.InvalidSender(from, VAULT); (, bytes memory userData, bytes4 networkId, address sender) = abi.decode( data, (bytes1, bytes, bytes4, address) ); - require(networkId == PNETWORK_REF_NETWORK_ID, "Invalid source network ID"); - require(sender == REPORTER_ADDRESS, "Invalid reporter"); + if (networkId != PNETWORK_REF_NETWORK_ID) revert Errors.InvalidNetworkId(networkId, PNETWORK_REF_NETWORK_ID); + if (sender != REPORTER_ADDRESS) revert Errors.UnauthorizedPNetworkReceive(); _receivePayload(userData); } } diff --git a/packages/evm/contracts/adapters/pnetwork/PNetworkBase.sol b/packages/evm/contracts/adapters/pnetwork/PNetworkBase.sol index 8a0bdf6d..3612b94f 100644 --- a/packages/evm/contracts/adapters/pnetwork/PNetworkBase.sol +++ b/packages/evm/contracts/adapters/pnetwork/PNetworkBase.sol @@ -4,14 +4,37 @@ pragma solidity ^0.8.17; import "@openzeppelin/contracts/interfaces/IERC777Recipient.sol"; import "@openzeppelin/contracts-upgradeable/interfaces/IERC1820RegistryUpgradeable.sol"; +import "./Errors.sol"; + abstract contract PNetworkBase is IERC777Recipient { + address public immutable VAULT; + address public immutable TOKEN; bytes4 public immutable PNETWORK_REF_NETWORK_ID; IERC1820RegistryUpgradeable private constant ERC1820 = IERC1820RegistryUpgradeable(0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24); bytes32 private constant TOKENS_RECIPIENT_INTERFACE_HASH = keccak256("ERC777TokensRecipient"); - constructor(bytes4 pNetworkRefNetworkId) { + constructor(address pNetworkVault, address pNetworkToken, bytes4 pNetworkRefNetworkId) { + VAULT = pNetworkVault; + TOKEN = pNetworkToken; PNETWORK_REF_NETWORK_ID = pNetworkRefNetworkId; ERC1820.setInterfaceImplementer(address(this), TOKENS_RECIPIENT_INTERFACE_HASH, address(this)); } + + // Implement the ERC777TokensRecipient interface + function tokensReceived( + address, + address, + address to, + uint256, + bytes calldata, + bytes calldata + ) external virtual onlySupportedToken(msg.sender) { + if (to != address(this)) revert Errors.InvalidReceiver(to, address(this)); + } + + modifier onlySupportedToken(address _tokenAddress) { + if (_tokenAddress != TOKEN) revert Errors.InvalidToken(_tokenAddress, TOKEN); + _; + } } diff --git a/packages/evm/contracts/adapters/pnetwork/PNetworkReporter.sol b/packages/evm/contracts/adapters/pnetwork/PNetworkReporter.sol index bb9e2a97..6ccf05f7 100644 --- a/packages/evm/contracts/adapters/pnetwork/PNetworkReporter.sol +++ b/packages/evm/contracts/adapters/pnetwork/PNetworkReporter.sol @@ -10,30 +10,11 @@ import { PNetworkBase } from "./PNetworkBase.sol"; abstract contract PNetworkReporter is PNetworkBase { uint256 private constant SWAP_AMOUNT = 100; - address public immutable VAULT; - address public immutable TOKEN; - - constructor(address vault, address token, bytes4 pNetworkAdapterNetworkId) PNetworkBase(pNetworkAdapterNetworkId) { - VAULT = vault; - TOKEN = token; - } - - // Implement the ERC777TokensRecipient interface - function tokensReceived( - address, - address, - address to, - uint256, - bytes calldata, - bytes calldata - ) external view onlySupportedToken(msg.sender) { - require(to == address(this), "Token receiver is not this contract"); - } - - modifier onlySupportedToken(address _tokenAddress) { - require(_tokenAddress == TOKEN, "Token at supplied address is NOT supported!"); - _; - } + constructor( + address pNetworkVault, + address pNetworkToken, + bytes4 pNetworkAdapterNetworkId + ) PNetworkBase(pNetworkVault, pNetworkToken, pNetworkAdapterNetworkId) {} function _char(bytes1 b) internal pure returns (bytes1 c) { if (uint8(b) < 10) return bytes1(uint8(b) + 0x30); diff --git a/packages/evm/test/adapters/pnetwork/02_PNetworkAdapter.spec.ts b/packages/evm/test/adapters/pnetwork/02_PNetworkAdapter.spec.ts index a68b08a7..75012675 100644 --- a/packages/evm/test/adapters/pnetwork/02_PNetworkAdapter.spec.ts +++ b/packages/evm/test/adapters/pnetwork/02_PNetworkAdapter.spec.ts @@ -46,7 +46,7 @@ describe("PNetworkAdapter", function () { expect(await pNetworkAdapter.deployed()) expect(await pNetworkAdapter.TOKEN()).to.equal(pToken.address) - expect(await pNetworkAdapter.ADMITTED_SENDER()).to.equal(ZERO_ADDRESS) + expect(await pNetworkAdapter.VAULT()).to.equal(ZERO_ADDRESS) }) }) @@ -69,7 +69,7 @@ describe("PNetworkAdapter", function () { }) it("Should not store hashes when receiving another token", async function () { - const { pNetworkAdapter, wallet } = await setup() + const { pNetworkAdapter, wallet, pToken } = await setup() const PToken = await ethers.getContractFactory("PToken") const fakePToken = await PToken.deploy("pToken", "P", []) @@ -83,9 +83,9 @@ describe("PNetworkAdapter", function () { ], ) const data = encodeToMetadata(userData, "0x005fe7f9", REPORTER_ADDRESS) - await expect(fakePToken.connect(wallet).mint(pNetworkAdapter.address, 1000, data, "0x")).to.be.revertedWith( - "Only accepted token is allowed", - ) + await expect(fakePToken.connect(wallet).mint(pNetworkAdapter.address, 1000, data, "0x")) + .to.be.revertedWithCustomError(pNetworkAdapter, "InvalidToken") + .withArgs(fakePToken.address, pToken.address) expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_ONE)).to.equal(HASH_ZERO) expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_TWO)).to.equal(HASH_ZERO) }) @@ -104,7 +104,7 @@ describe("PNetworkAdapter", function () { const data = encodeToMetadata(userData, "0x005fe7f9", REPORTER_ADDRESS) await expect(pToken.connect(wallet).send(pNetworkAdapter.address, 1000, data)) .to.be.revertedWithCustomError(pNetworkAdapter, "InvalidSender") - .withArgs(wallet.address) + .withArgs(wallet.address, ZERO_ADDRESS) expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_ONE)).to.equal(HASH_ZERO) expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_TWO)).to.equal(HASH_ZERO) }) @@ -185,7 +185,7 @@ describe("PNetworkAdapter", function () { const { pNetworkAdapter, erc777Token, vault } = await setup() expect(await pNetworkAdapter.deployed()) expect(await pNetworkAdapter.TOKEN()).to.equal(erc777Token.address) - expect(await pNetworkAdapter.ADMITTED_SENDER()).to.equal(vault.address) + expect(await pNetworkAdapter.VAULT()).to.equal(vault.address) }) }) @@ -208,7 +208,7 @@ describe("PNetworkAdapter", function () { }) it("Should not store hashes when receiving another token", async function () { - const { pNetworkAdapter, wallet, vault, anotherErc777Token } = await setup() + const { pNetworkAdapter, wallet, vault, erc777Token, anotherErc777Token } = await setup() const coder = new ethers.utils.AbiCoder() const userData = coder.encode( @@ -219,15 +219,15 @@ describe("PNetworkAdapter", function () { ], ) const data = encodeToMetadata(userData, "0x005fe7f9", REPORTER_ADDRESS) - await expect( - vault.connect(wallet).pegOut(pNetworkAdapter.address, anotherErc777Token.address, 100, data), - ).to.be.revertedWith("Only accepted token is allowed") + await expect(vault.connect(wallet).pegOut(pNetworkAdapter.address, anotherErc777Token.address, 100, data)) + .to.be.revertedWithCustomError(pNetworkAdapter, "InvalidToken") + .withArgs(anotherErc777Token.address, erc777Token.address) expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_ONE)).to.equal(HASH_ZERO) expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_TWO)).to.equal(HASH_ZERO) }) it("Should not store hashes when tokens are not sent by the vault", async function () { - const { pNetworkAdapter, wallet, erc777Token } = await setup() + const { pNetworkAdapter, wallet, erc777Token, vault } = await setup() const coder = new ethers.utils.AbiCoder() const userData = coder.encode( @@ -240,7 +240,7 @@ describe("PNetworkAdapter", function () { const data = encodeToMetadata(userData, "0x005fe7f9", REPORTER_ADDRESS) await expect(erc777Token.connect(wallet).send(pNetworkAdapter.address, 100, data)) .to.be.revertedWithCustomError(pNetworkAdapter, "InvalidSender") - .withArgs(wallet.address) + .withArgs(wallet.address, vault.address) expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_ONE)).to.equal(HASH_ZERO) expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_TWO)).to.equal(HASH_ZERO) }) @@ -257,9 +257,9 @@ describe("PNetworkAdapter", function () { ], ) const data = encodeToMetadata(userData, "0x00e4b170", REPORTER_ADDRESS) - await expect( - vault.connect(wallet).pegOut(pNetworkAdapter.address, erc777Token.address, 100, data), - ).to.be.revertedWith("Invalid source network ID") + await expect(vault.connect(wallet).pegOut(pNetworkAdapter.address, erc777Token.address, 100, data)) + .to.be.revertedWithCustomError(pNetworkAdapter, "InvalidNetworkId") + .withArgs("0x00e4b170", "0x005fe7f9") expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_ONE)).to.equal(HASH_ZERO) expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_TWO)).to.equal(HASH_ZERO) }) @@ -277,9 +277,9 @@ describe("PNetworkAdapter", function () { ], ) const data = encodeToMetadata(userData, "0x005fe7f9", WRONG_REPORTER_ADDRESS) - await expect( - vault.connect(wallet).pegOut(pNetworkAdapter.address, erc777Token.address, 100, data), - ).to.be.revertedWith("Invalid reporter") + await expect(vault.connect(wallet).pegOut(pNetworkAdapter.address, erc777Token.address, 100, data)) + .to.be.revertedWithCustomError(pNetworkAdapter, "UnauthorizedPNetworkReceive") + .withArgs() expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_ONE)).to.equal(HASH_ZERO) expect(await pNetworkAdapter.getHashFromOracle(DOMAIN_ID, ID_TWO)).to.equal(HASH_ZERO) }) From 58e85560e56c3aff81e4b821934095d91902fe71 Mon Sep 17 00:00:00 2001 From: Alain Olivier Date: Mon, 11 Dec 2023 15:08:53 +0100 Subject: [PATCH 10/19] refactor(evm): rename pNetwork constructor arguments --- .../evm/contracts/adapters/pnetwork/PNetworkAdapter.sol | 6 +++--- .../adapters/pnetwork/PNetworkHeaderReporter.sol | 9 ++++++--- .../adapters/pnetwork/PNetworkMessageRelayer.sol | 6 +++--- .../evm/contracts/adapters/pnetwork/PNetworkReporter.sol | 2 +- 4 files changed, 13 insertions(+), 10 deletions(-) diff --git a/packages/evm/contracts/adapters/pnetwork/PNetworkAdapter.sol b/packages/evm/contracts/adapters/pnetwork/PNetworkAdapter.sol index 38ce06f7..8197dd12 100644 --- a/packages/evm/contracts/adapters/pnetwork/PNetworkAdapter.sol +++ b/packages/evm/contracts/adapters/pnetwork/PNetworkAdapter.sol @@ -13,11 +13,11 @@ contract PNetworkAdapter is HeaderOracleAdapter, PNetworkBase { address reporterAddress, address pNetworkVault, address pNetworkToken, - bytes4 pNetworkSourceNetworkId + bytes4 pNetworkReporterNetworkId ) HeaderOracleAdapter(reporterChain, reporterAddress) - PNetworkBase(pNetworkVault, pNetworkToken, pNetworkSourceNetworkId) - {} + PNetworkBase(pNetworkVault, pNetworkToken, pNetworkReporterNetworkId) + {} // solhint-disable no-empty-blocks // Implement the ERC777TokensRecipient interface function tokensReceived( diff --git a/packages/evm/contracts/adapters/pnetwork/PNetworkHeaderReporter.sol b/packages/evm/contracts/adapters/pnetwork/PNetworkHeaderReporter.sol index a8af946d..40e35bdb 100644 --- a/packages/evm/contracts/adapters/pnetwork/PNetworkHeaderReporter.sol +++ b/packages/evm/contracts/adapters/pnetwork/PNetworkHeaderReporter.sol @@ -8,10 +8,13 @@ contract PNetworkHeaderReporter is HeaderReporter, PNetworkReporter { constructor( address headerStorage, uint64 adapterChain, - address vault, - address token, + address pNetworkVault, + address pNetworkToken, bytes4 pNetworkAdapterNetworkId - ) HeaderReporter(headerStorage, adapterChain) PNetworkReporter(vault, token, pNetworkAdapterNetworkId) {} // solhint-disable no-empty-blocks + ) + HeaderReporter(headerStorage, adapterChain) + PNetworkReporter(pNetworkVault, pNetworkToken, pNetworkAdapterNetworkId) + {} // solhint-disable no-empty-blocks function _sendPayload(bytes memory payload, address adapter) internal override { _pNetworkSend(payload, adapter); diff --git a/packages/evm/contracts/adapters/pnetwork/PNetworkMessageRelayer.sol b/packages/evm/contracts/adapters/pnetwork/PNetworkMessageRelayer.sol index b68ebf09..40d081d1 100644 --- a/packages/evm/contracts/adapters/pnetwork/PNetworkMessageRelayer.sol +++ b/packages/evm/contracts/adapters/pnetwork/PNetworkMessageRelayer.sol @@ -8,10 +8,10 @@ contract PNetworkMessageRelay is MessageRelay, PNetworkReporter { constructor( address yaho, uint64 adapterChain, - address vault, - address token, + address pNetworkVault, + address pNetworktoken, bytes4 pNetworkAdapterNetworkId - ) MessageRelay(yaho, adapterChain) PNetworkReporter(vault, token, pNetworkAdapterNetworkId) {} // solhint-disable no-empty-blocks + ) MessageRelay(yaho, adapterChain) PNetworkReporter(pNetworkVault, pNetworktoken, pNetworkAdapterNetworkId) {} // solhint-disable no-empty-blocks function _sendPayload(bytes memory payload, address adapter) internal override { _pNetworkSend(payload, adapter); diff --git a/packages/evm/contracts/adapters/pnetwork/PNetworkReporter.sol b/packages/evm/contracts/adapters/pnetwork/PNetworkReporter.sol index 6ccf05f7..f271f0e3 100644 --- a/packages/evm/contracts/adapters/pnetwork/PNetworkReporter.sol +++ b/packages/evm/contracts/adapters/pnetwork/PNetworkReporter.sol @@ -14,7 +14,7 @@ abstract contract PNetworkReporter is PNetworkBase { address pNetworkVault, address pNetworkToken, bytes4 pNetworkAdapterNetworkId - ) PNetworkBase(pNetworkVault, pNetworkToken, pNetworkAdapterNetworkId) {} + ) PNetworkBase(pNetworkVault, pNetworkToken, pNetworkAdapterNetworkId) {} // solhint-disable no-empty-blocks function _char(bytes1 b) internal pure returns (bytes1 c) { if (uint8(b) < 10) return bytes1(uint8(b) + 0x30); From d18c2d0d4e41eeabcc1a69bd4c95caa0a9471cff Mon Sep 17 00:00:00 2001 From: Alain Olivier Date: Mon, 11 Dec 2023 15:43:51 +0100 Subject: [PATCH 11/19] refactor(evm): remove pNetwork errors file --- packages/evm/contracts/adapters/pnetwork/Errors.sol | 11 ----------- .../contracts/adapters/pnetwork/PNetworkAdapter.sol | 11 +++++++---- .../evm/contracts/adapters/pnetwork/PNetworkBase.sol | 9 +++++---- 3 files changed, 12 insertions(+), 19 deletions(-) delete mode 100644 packages/evm/contracts/adapters/pnetwork/Errors.sol diff --git a/packages/evm/contracts/adapters/pnetwork/Errors.sol b/packages/evm/contracts/adapters/pnetwork/Errors.sol deleted file mode 100644 index 84e3f847..00000000 --- a/packages/evm/contracts/adapters/pnetwork/Errors.sol +++ /dev/null @@ -1,11 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -pragma solidity ^0.8.17; - -library Errors { - error ArrayLengthMismatch(); - error InvalidToken(address token, address expected); - error InvalidSender(address sender, address expected); - error InvalidReceiver(address receiver, address expected); - error InvalidNetworkId(bytes4 networkId, bytes4 expected); - error UnauthorizedPNetworkReceive(); -} diff --git a/packages/evm/contracts/adapters/pnetwork/PNetworkAdapter.sol b/packages/evm/contracts/adapters/pnetwork/PNetworkAdapter.sol index 8197dd12..5f13a6f5 100644 --- a/packages/evm/contracts/adapters/pnetwork/PNetworkAdapter.sol +++ b/packages/evm/contracts/adapters/pnetwork/PNetworkAdapter.sol @@ -5,9 +5,12 @@ import "@openzeppelin/contracts/token/ERC777/presets/ERC777PresetFixedSupply.sol import { HeaderOracleAdapter } from "../HeaderOracleAdapter.sol"; import { PNetworkBase } from "./PNetworkBase.sol"; -import { Errors } from "./Errors.sol"; contract PNetworkAdapter is HeaderOracleAdapter, PNetworkBase { + error InvalidSender(address sender, address expected); + error InvalidNetworkId(bytes4 networkId, bytes4 expected); + error UnauthorizedPNetworkReceive(); + constructor( uint256 reporterChain, address reporterAddress, @@ -28,13 +31,13 @@ contract PNetworkAdapter is HeaderOracleAdapter, PNetworkBase { bytes calldata data, bytes calldata ) external override onlySupportedToken(msg.sender) { - if (from != VAULT) revert Errors.InvalidSender(from, VAULT); + if (from != VAULT) revert InvalidSender(from, VAULT); (, bytes memory userData, bytes4 networkId, address sender) = abi.decode( data, (bytes1, bytes, bytes4, address) ); - if (networkId != PNETWORK_REF_NETWORK_ID) revert Errors.InvalidNetworkId(networkId, PNETWORK_REF_NETWORK_ID); - if (sender != REPORTER_ADDRESS) revert Errors.UnauthorizedPNetworkReceive(); + if (networkId != PNETWORK_REF_NETWORK_ID) revert InvalidNetworkId(networkId, PNETWORK_REF_NETWORK_ID); + if (sender != REPORTER_ADDRESS) revert UnauthorizedPNetworkReceive(); _receivePayload(userData); } } diff --git a/packages/evm/contracts/adapters/pnetwork/PNetworkBase.sol b/packages/evm/contracts/adapters/pnetwork/PNetworkBase.sol index 3612b94f..70c8ace2 100644 --- a/packages/evm/contracts/adapters/pnetwork/PNetworkBase.sol +++ b/packages/evm/contracts/adapters/pnetwork/PNetworkBase.sol @@ -4,8 +4,6 @@ pragma solidity ^0.8.17; import "@openzeppelin/contracts/interfaces/IERC777Recipient.sol"; import "@openzeppelin/contracts-upgradeable/interfaces/IERC1820RegistryUpgradeable.sol"; -import "./Errors.sol"; - abstract contract PNetworkBase is IERC777Recipient { address public immutable VAULT; address public immutable TOKEN; @@ -14,6 +12,9 @@ abstract contract PNetworkBase is IERC777Recipient { IERC1820RegistryUpgradeable(0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24); bytes32 private constant TOKENS_RECIPIENT_INTERFACE_HASH = keccak256("ERC777TokensRecipient"); + error InvalidToken(address token, address expected); + error InvalidReceiver(address receiver, address expected); + constructor(address pNetworkVault, address pNetworkToken, bytes4 pNetworkRefNetworkId) { VAULT = pNetworkVault; TOKEN = pNetworkToken; @@ -30,11 +31,11 @@ abstract contract PNetworkBase is IERC777Recipient { bytes calldata, bytes calldata ) external virtual onlySupportedToken(msg.sender) { - if (to != address(this)) revert Errors.InvalidReceiver(to, address(this)); + if (to != address(this)) revert InvalidReceiver(to, address(this)); } modifier onlySupportedToken(address _tokenAddress) { - if (_tokenAddress != TOKEN) revert Errors.InvalidToken(_tokenAddress, TOKEN); + if (_tokenAddress != TOKEN) revert InvalidToken(_tokenAddress, TOKEN); _; } } From 5ba9ba347a791a2e8b5248ff9ac9b877fcbad0f9 Mon Sep 17 00:00:00 2001 From: Alain Olivier Date: Mon, 11 Dec 2023 15:44:47 +0100 Subject: [PATCH 12/19] refactor(evm): remove unnecessary import in pNetwork adapter --- packages/evm/contracts/adapters/pnetwork/PNetworkAdapter.sol | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/evm/contracts/adapters/pnetwork/PNetworkAdapter.sol b/packages/evm/contracts/adapters/pnetwork/PNetworkAdapter.sol index 5f13a6f5..96d28a3f 100644 --- a/packages/evm/contracts/adapters/pnetwork/PNetworkAdapter.sol +++ b/packages/evm/contracts/adapters/pnetwork/PNetworkAdapter.sol @@ -1,8 +1,6 @@ // SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.17; -import "@openzeppelin/contracts/token/ERC777/presets/ERC777PresetFixedSupply.sol"; - import { HeaderOracleAdapter } from "../HeaderOracleAdapter.sol"; import { PNetworkBase } from "./PNetworkBase.sol"; From 3588e531b7fe7657733114646018d1c1799033bf Mon Sep 17 00:00:00 2001 From: Alain Olivier Date: Mon, 11 Dec 2023 15:50:04 +0100 Subject: [PATCH 13/19] refactor(evm): rename pNetwork message relay file --- .../{PNetworkMessageRelayer.sol => PNetworkMessageRelay.sol} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename packages/evm/contracts/adapters/pnetwork/{PNetworkMessageRelayer.sol => PNetworkMessageRelay.sol} (100%) diff --git a/packages/evm/contracts/adapters/pnetwork/PNetworkMessageRelayer.sol b/packages/evm/contracts/adapters/pnetwork/PNetworkMessageRelay.sol similarity index 100% rename from packages/evm/contracts/adapters/pnetwork/PNetworkMessageRelayer.sol rename to packages/evm/contracts/adapters/pnetwork/PNetworkMessageRelay.sol From c1dd5599f93e90829eabda61f22b005b4fd72f60 Mon Sep 17 00:00:00 2001 From: Alain Olivier Date: Mon, 11 Dec 2023 15:52:17 +0100 Subject: [PATCH 14/19] chore(evm): fix pNetwork-related deploy tasks --- packages/evm/tasks/deploy/adapters/pnetwork.ts | 10 ++++++++-- .../adapters/pnetwork/01_PNetworkMessageRelay.spec.ts | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/packages/evm/tasks/deploy/adapters/pnetwork.ts b/packages/evm/tasks/deploy/adapters/pnetwork.ts index a0ff0427..18d8e7bb 100644 --- a/packages/evm/tasks/deploy/adapters/pnetwork.ts +++ b/packages/evm/tasks/deploy/adapters/pnetwork.ts @@ -4,10 +4,10 @@ import type { TaskArguments } from "hardhat/types" import type { PNetworkAdapter } from "../../../types/contracts/adapters/pnetwork/PNetworkAdapter" import type { PNetworkHeaderReporter } from "../../../types/contracts/adapters/pnetwork/PNetworkHeaderReporter" -import type { PNetworkMessageRelay } from "../../../types/contracts/adapters/pnetwork/PNetworkMessageRelayer.sol" +import type { PNetworkMessageRelay } from "../../../types/contracts/adapters/pnetwork/PNetworkMessageRelay" import type { PNetworkAdapter__factory } from "../../../types/factories/contracts/adapters/pnetwork/PNetworkAdapter__factory" import type { PNetworkHeaderReporter__factory } from "../../../types/factories/contracts/adapters/pnetwork/PNetworkHeaderReporter__factory" -import type { PNetworkMessageRelay__factory } from "../../../types/factories/contracts/adapters/pnetwork/PNetworkMessageRelayer.sol" +import type { PNetworkMessageRelay__factory } from "../../../types/factories/contracts/adapters/pnetwork/PNetworkMessageRelay__factory" import { verify } from "../index" task("deploy:adapter:PNetworkHeaderReporter") @@ -15,6 +15,7 @@ task("deploy:adapter:PNetworkHeaderReporter") .addParam("chainId", "chain id of the adapter contract") .addParam("vault", "address of the vault contract (address 0 when deploying on non-native chain)") .addParam("token", "address of the token used to transfer data") + .addParam("adapterNetworkId", "pNetwork network ID of the adapter chain") .addFlag("verify", "whether to verify the contract on Etherscan") .setAction(async function (taskArguments: TaskArguments, hre) { console.log("Deploying PNetworkHeaderReporter...") @@ -27,6 +28,7 @@ task("deploy:adapter:PNetworkHeaderReporter") taskArguments.chainId, taskArguments.vault, taskArguments.token, + taskArguments.adapterNetworkId, ] as const const pNetworkHeaderReporter: PNetworkHeaderReporter = ( await pNetworkHeaderReporterFactory.connect(signers[0]).deploy(...constructorArguments) @@ -42,6 +44,7 @@ task("deploy:adapter:PNetworkMessageRelay") .addParam("chainId", "chain id of the adapter contract") .addParam("vault", "address of the vault contract (address 0 when deploying on non-native chain)") .addParam("token", "address of the token used to transfer data") + .addParam("adapterNetworkId", "pNetwork network ID of the adapter chain") .addFlag("verify", "whether to verify the contract on Etherscan") .setAction(async function (taskArguments: TaskArguments, hre) { console.log("Deploying PNetworkMessageRelay...") @@ -54,6 +57,7 @@ task("deploy:adapter:PNetworkMessageRelay") taskArguments.chainId, taskArguments.vault, taskArguments.token, + taskArguments.adapterNetworkId, ] as const const pNetworkMessageRelay: PNetworkMessageRelay = ( await pNetworkAdapterFactory.connect(signers[0]).deploy(...constructorArguments) @@ -69,6 +73,7 @@ task("deploy:adapter:PNetworkAdapter") .addParam("reporter", "address of the reporter contract") .addParam("vault", "address of the vault contract (address 0 when deploying on non-native chain)") .addParam("token", "address of the token used to transfer data") + .addParam("reporterNetworkId", "pNetwork network ID of the reporter chain") .addFlag("verify", "whether to verify the contract on Etherscan") .setAction(async function (taskArguments: TaskArguments, hre) { console.log("Deploying PNetworkAdapter...") @@ -81,6 +86,7 @@ task("deploy:adapter:PNetworkAdapter") taskArguments.reporter, taskArguments.vault, taskArguments.token, + taskArguments.reporterNetworkId, ] as const const pNetworkAdapter: PNetworkAdapter = ( await pNetworkAdapterFactory.connect(signers[0]).deploy(...constructorArguments) diff --git a/packages/evm/test/adapters/pnetwork/01_PNetworkMessageRelay.spec.ts b/packages/evm/test/adapters/pnetwork/01_PNetworkMessageRelay.spec.ts index dd759960..be51035d 100644 --- a/packages/evm/test/adapters/pnetwork/01_PNetworkMessageRelay.spec.ts +++ b/packages/evm/test/adapters/pnetwork/01_PNetworkMessageRelay.spec.ts @@ -5,7 +5,7 @@ import { ZERO_ADDRESS, deployErc1820Registry, resetNetwork } from "./utils.spec" const DOMAIN_ID = "0x0000000000000000000000000000000000000000000000000000000000001" -describe("PNetworkMessageRelayer", function () { +describe("PNetworkMessageRelay", function () { describe("Native Network", () => { const setup = async () => { await resetNetwork() From 454c46dc8d401d346a1b3cebf1945a39e53615d6 Mon Sep 17 00:00:00 2001 From: Alain Olivier Date: Mon, 11 Dec 2023 16:07:54 +0100 Subject: [PATCH 15/19] refactor(evm): rename pNetwork folder --- .../contracts/adapters/{pnetwork => PNetwork}/PNetworkAdapter.sol | 0 .../contracts/adapters/{pnetwork => PNetwork}/PNetworkBase.sol | 0 .../adapters/{pnetwork => PNetwork}/PNetworkHeaderReporter.sol | 0 .../adapters/{pnetwork => PNetwork}/PNetworkMessageRelay.sol | 0 .../adapters/{pnetwork => PNetwork}/PNetworkReporter.sol | 0 .../adapters/{pnetwork => PNetwork}/interfaces/IErc20Vault.sol | 0 .../adapters/{pnetwork => PNetwork}/interfaces/IPToken.sol | 0 .../adapters/{pnetwork => PNetwork}/test/ERC777Token.sol | 0 .../contracts/adapters/{pnetwork => PNetwork}/test/MockVault.sol | 0 .../evm/contracts/adapters/{pnetwork => PNetwork}/test/PToken.sol | 0 .../{pnetwork => PNetwork}/01_PNetworkMessageRelay.spec.ts | 0 .../adapters/{pnetwork => PNetwork}/02_PNetworkAdapter.spec.ts | 0 packages/evm/test/adapters/{pnetwork => PNetwork}/utils.spec.ts | 0 13 files changed, 0 insertions(+), 0 deletions(-) rename packages/evm/contracts/adapters/{pnetwork => PNetwork}/PNetworkAdapter.sol (100%) rename packages/evm/contracts/adapters/{pnetwork => PNetwork}/PNetworkBase.sol (100%) rename packages/evm/contracts/adapters/{pnetwork => PNetwork}/PNetworkHeaderReporter.sol (100%) rename packages/evm/contracts/adapters/{pnetwork => PNetwork}/PNetworkMessageRelay.sol (100%) rename packages/evm/contracts/adapters/{pnetwork => PNetwork}/PNetworkReporter.sol (100%) rename packages/evm/contracts/adapters/{pnetwork => PNetwork}/interfaces/IErc20Vault.sol (100%) rename packages/evm/contracts/adapters/{pnetwork => PNetwork}/interfaces/IPToken.sol (100%) rename packages/evm/contracts/adapters/{pnetwork => PNetwork}/test/ERC777Token.sol (100%) rename packages/evm/contracts/adapters/{pnetwork => PNetwork}/test/MockVault.sol (100%) rename packages/evm/contracts/adapters/{pnetwork => PNetwork}/test/PToken.sol (100%) rename packages/evm/test/adapters/{pnetwork => PNetwork}/01_PNetworkMessageRelay.spec.ts (100%) rename packages/evm/test/adapters/{pnetwork => PNetwork}/02_PNetworkAdapter.spec.ts (100%) rename packages/evm/test/adapters/{pnetwork => PNetwork}/utils.spec.ts (100%) diff --git a/packages/evm/contracts/adapters/pnetwork/PNetworkAdapter.sol b/packages/evm/contracts/adapters/PNetwork/PNetworkAdapter.sol similarity index 100% rename from packages/evm/contracts/adapters/pnetwork/PNetworkAdapter.sol rename to packages/evm/contracts/adapters/PNetwork/PNetworkAdapter.sol diff --git a/packages/evm/contracts/adapters/pnetwork/PNetworkBase.sol b/packages/evm/contracts/adapters/PNetwork/PNetworkBase.sol similarity index 100% rename from packages/evm/contracts/adapters/pnetwork/PNetworkBase.sol rename to packages/evm/contracts/adapters/PNetwork/PNetworkBase.sol diff --git a/packages/evm/contracts/adapters/pnetwork/PNetworkHeaderReporter.sol b/packages/evm/contracts/adapters/PNetwork/PNetworkHeaderReporter.sol similarity index 100% rename from packages/evm/contracts/adapters/pnetwork/PNetworkHeaderReporter.sol rename to packages/evm/contracts/adapters/PNetwork/PNetworkHeaderReporter.sol diff --git a/packages/evm/contracts/adapters/pnetwork/PNetworkMessageRelay.sol b/packages/evm/contracts/adapters/PNetwork/PNetworkMessageRelay.sol similarity index 100% rename from packages/evm/contracts/adapters/pnetwork/PNetworkMessageRelay.sol rename to packages/evm/contracts/adapters/PNetwork/PNetworkMessageRelay.sol diff --git a/packages/evm/contracts/adapters/pnetwork/PNetworkReporter.sol b/packages/evm/contracts/adapters/PNetwork/PNetworkReporter.sol similarity index 100% rename from packages/evm/contracts/adapters/pnetwork/PNetworkReporter.sol rename to packages/evm/contracts/adapters/PNetwork/PNetworkReporter.sol diff --git a/packages/evm/contracts/adapters/pnetwork/interfaces/IErc20Vault.sol b/packages/evm/contracts/adapters/PNetwork/interfaces/IErc20Vault.sol similarity index 100% rename from packages/evm/contracts/adapters/pnetwork/interfaces/IErc20Vault.sol rename to packages/evm/contracts/adapters/PNetwork/interfaces/IErc20Vault.sol diff --git a/packages/evm/contracts/adapters/pnetwork/interfaces/IPToken.sol b/packages/evm/contracts/adapters/PNetwork/interfaces/IPToken.sol similarity index 100% rename from packages/evm/contracts/adapters/pnetwork/interfaces/IPToken.sol rename to packages/evm/contracts/adapters/PNetwork/interfaces/IPToken.sol diff --git a/packages/evm/contracts/adapters/pnetwork/test/ERC777Token.sol b/packages/evm/contracts/adapters/PNetwork/test/ERC777Token.sol similarity index 100% rename from packages/evm/contracts/adapters/pnetwork/test/ERC777Token.sol rename to packages/evm/contracts/adapters/PNetwork/test/ERC777Token.sol diff --git a/packages/evm/contracts/adapters/pnetwork/test/MockVault.sol b/packages/evm/contracts/adapters/PNetwork/test/MockVault.sol similarity index 100% rename from packages/evm/contracts/adapters/pnetwork/test/MockVault.sol rename to packages/evm/contracts/adapters/PNetwork/test/MockVault.sol diff --git a/packages/evm/contracts/adapters/pnetwork/test/PToken.sol b/packages/evm/contracts/adapters/PNetwork/test/PToken.sol similarity index 100% rename from packages/evm/contracts/adapters/pnetwork/test/PToken.sol rename to packages/evm/contracts/adapters/PNetwork/test/PToken.sol diff --git a/packages/evm/test/adapters/pnetwork/01_PNetworkMessageRelay.spec.ts b/packages/evm/test/adapters/PNetwork/01_PNetworkMessageRelay.spec.ts similarity index 100% rename from packages/evm/test/adapters/pnetwork/01_PNetworkMessageRelay.spec.ts rename to packages/evm/test/adapters/PNetwork/01_PNetworkMessageRelay.spec.ts diff --git a/packages/evm/test/adapters/pnetwork/02_PNetworkAdapter.spec.ts b/packages/evm/test/adapters/PNetwork/02_PNetworkAdapter.spec.ts similarity index 100% rename from packages/evm/test/adapters/pnetwork/02_PNetworkAdapter.spec.ts rename to packages/evm/test/adapters/PNetwork/02_PNetworkAdapter.spec.ts diff --git a/packages/evm/test/adapters/pnetwork/utils.spec.ts b/packages/evm/test/adapters/PNetwork/utils.spec.ts similarity index 100% rename from packages/evm/test/adapters/pnetwork/utils.spec.ts rename to packages/evm/test/adapters/PNetwork/utils.spec.ts From 8b3581d7a248c01b1822bfa5b60abbddc8983569 Mon Sep 17 00:00:00 2001 From: Alain Olivier Date: Mon, 11 Dec 2023 17:12:48 +0100 Subject: [PATCH 16/19] refactor(evm): reduce PNT swap amount in pNetwork reporter --- packages/evm/contracts/adapters/PNetwork/PNetworkReporter.sol | 2 +- .../test/adapters/PNetwork/01_PNetworkMessageRelay.spec.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/evm/contracts/adapters/PNetwork/PNetworkReporter.sol b/packages/evm/contracts/adapters/PNetwork/PNetworkReporter.sol index f271f0e3..27b2bb32 100644 --- a/packages/evm/contracts/adapters/PNetwork/PNetworkReporter.sol +++ b/packages/evm/contracts/adapters/PNetwork/PNetworkReporter.sol @@ -8,7 +8,7 @@ import { IPToken } from "./interfaces/IPToken.sol"; import { PNetworkBase } from "./PNetworkBase.sol"; abstract contract PNetworkReporter is PNetworkBase { - uint256 private constant SWAP_AMOUNT = 100; + uint256 private constant SWAP_AMOUNT = 1; constructor( address pNetworkVault, diff --git a/packages/evm/test/adapters/PNetwork/01_PNetworkMessageRelay.spec.ts b/packages/evm/test/adapters/PNetwork/01_PNetworkMessageRelay.spec.ts index be51035d..8bb69bfa 100644 --- a/packages/evm/test/adapters/PNetwork/01_PNetworkMessageRelay.spec.ts +++ b/packages/evm/test/adapters/PNetwork/01_PNetworkMessageRelay.spec.ts @@ -86,7 +86,7 @@ describe("PNetworkMessageRelay", function () { .withArgs( erc777Token.address, pNetworkMessageRelay.address, - 100, + 1, pNetworkAdapter.address.replace("0x", "").toLowerCase(), expectedUserData, "0x12345678", @@ -173,7 +173,7 @@ describe("PNetworkMessageRelay", function () { .and.to.emit(pToken, "Redeem") .withArgs( pNetworkMessageRelay.address, - 100, + 1, pNetworkAdapter.address.replace("0x", "").toLowerCase(), expectedUserData, "0x87654321", From c0050899d424074f5b5431574a4429c5fd73ee34 Mon Sep 17 00:00:00 2001 From: Alain Olivier Date: Mon, 11 Dec 2023 21:00:42 +0100 Subject: [PATCH 17/19] test(evm): add tests for pNetwork header reporter --- .../02_PNetworkHeaderReporter.spec.ts | 173 ++++++++++++++++++ ...ter.spec.ts => 03_PNetworkAdapter.spec.ts} | 0 2 files changed, 173 insertions(+) create mode 100644 packages/evm/test/adapters/PNetwork/02_PNetworkHeaderReporter.spec.ts rename packages/evm/test/adapters/PNetwork/{02_PNetworkAdapter.spec.ts => 03_PNetworkAdapter.spec.ts} (100%) diff --git a/packages/evm/test/adapters/PNetwork/02_PNetworkHeaderReporter.spec.ts b/packages/evm/test/adapters/PNetwork/02_PNetworkHeaderReporter.spec.ts new file mode 100644 index 00000000..af1984cb --- /dev/null +++ b/packages/evm/test/adapters/PNetwork/02_PNetworkHeaderReporter.spec.ts @@ -0,0 +1,173 @@ +import { expect } from "chai" +import { ethers } from "hardhat" + +import { ZERO_ADDRESS, deployErc1820Registry, resetNetwork } from "./utils.spec" + +const DOMAIN_ID = "0x0000000000000000000000000000000000000000000000000000000000001" + +describe("PNetworkHeaderReporter", function () { + describe("Native Network", () => { + const setup = async () => { + await resetNetwork() + const [wallet] = await ethers.getSigners() + const HeaderStorage = await ethers.getContractFactory("HeaderStorage") + const headerStorage = await HeaderStorage.deploy() + await deployErc1820Registry(wallet) + const ERC777Token = await ethers.getContractFactory("ERC777Token") + const erc777Token = await ERC777Token.deploy("ERC777 Token", "E777", []) + const anotherErc777Token = await ERC777Token.deploy("Another ERC777 Token", "A777", []) + const Vault = await ethers.getContractFactory("MockVault") + const vault = await Vault.deploy() + await vault.initialize([erc777Token.address, anotherErc777Token.address], "0x12345678") + + const PNetworkHeaderReporter = await ethers.getContractFactory("PNetworkHeaderReporter") + const pNetworkHeaderReporter = await PNetworkHeaderReporter.deploy( + headerStorage.address, + DOMAIN_ID, + vault.address, + erc777Token.address, + "0x005fe7f9", + ) + + await erc777Token.connect(wallet).send(pNetworkHeaderReporter.address, 10000, "0x") + + const PNetworkAdapter = await ethers.getContractFactory("PNetworkAdapter") + const pNetworkAdapter = await PNetworkAdapter.deploy( + DOMAIN_ID, + pNetworkHeaderReporter.address, + vault.address, + pNetworkHeaderReporter.address, + "0x005fe7f9", + ) + + return { + erc777Token, + anotherErc777Token, + vault, + wallet, + headerStorage, + pNetworkHeaderReporter, + pNetworkAdapter, + } + } + + describe("Deploy", function () { + it("Successfully deploys contract", async function () { + const { pNetworkHeaderReporter, erc777Token, vault, headerStorage } = await setup() + expect(await pNetworkHeaderReporter.deployed()) + expect(await pNetworkHeaderReporter.TOKEN()).to.equal(erc777Token.address) + expect(await pNetworkHeaderReporter.VAULT()).to.equal(vault.address) + expect(await pNetworkHeaderReporter.HEADER_STORAGE()).to.equal(headerStorage.address) + }) + }) + + describe("reportHeaders()", function () { + it("Reports headers over pNetwork", async function () { + const { pNetworkHeaderReporter, pNetworkAdapter, vault, erc777Token, headerStorage } = await setup() + const blockIds = [0, 1] + const blocks = await Promise.all(blockIds.map((_id) => ethers.provider._getBlock(_id))) + const hashes = blocks.map((_block) => _block.hash) + const expectedUserData = new ethers.utils.AbiCoder().encode(["uint256[]", "bytes32[]"], [blockIds, hashes]) + await expect(pNetworkHeaderReporter.reportHeaders(blockIds, pNetworkAdapter.address)) + .to.emit(pNetworkHeaderReporter, "HeaderReported") + .withArgs(pNetworkHeaderReporter.address, blockIds[0], hashes[0]) + .and.to.emit(pNetworkHeaderReporter, "HeaderReported") + .withArgs(pNetworkHeaderReporter.address, blockIds[1], hashes[1]) + .and.to.emit(vault, "PegIn") + .withArgs( + erc777Token.address, + pNetworkHeaderReporter.address, + 1, + pNetworkAdapter.address.replace("0x", "").toLowerCase(), + expectedUserData, + "0x12345678", + "0x005fe7f9", + ) + expect(await headerStorage.headers(blockIds[0])).to.equal(hashes[0]) + expect(await headerStorage.headers(blockIds[1])).to.equal(hashes[1]) + }) + }) + }) + + describe("Host Network", () => { + const setup = async () => { + await resetNetwork() + const [wallet] = await ethers.getSigners() + const HeaderStorage = await ethers.getContractFactory("HeaderStorage") + const headerStorage = await HeaderStorage.deploy() + await deployErc1820Registry(wallet) + const PToken = await ethers.getContractFactory("PToken") + const pToken = await PToken.deploy("pToken", "P", []) + const anotherPToken = await PToken.deploy("Another ERC777 Token", "A777", []) + const Vault = await ethers.getContractFactory("MockVault") + const vault = await Vault.deploy() + await vault.initialize([pToken.address, anotherPToken.address], "0x12345678") + + const PNetworkHeaderReporter = await ethers.getContractFactory("PNetworkHeaderReporter") + const pNetworkHeaderReporter = await PNetworkHeaderReporter.deploy( + headerStorage.address, + DOMAIN_ID, + ZERO_ADDRESS, + pToken.address, + "0x005fe7f9", + ) + + await pToken.connect(wallet).send(pNetworkHeaderReporter.address, 10000, "0x") + + const PNetworkAdapter = await ethers.getContractFactory("PNetworkAdapter") + const pNetworkAdapter = await PNetworkAdapter.deploy( + DOMAIN_ID, + pNetworkHeaderReporter.address, + vault.address, + pNetworkHeaderReporter.address, + "0x005fe7f9", + ) + + return { + pToken, + anotherPToken, + vault, + wallet, + headerStorage, + pNetworkHeaderReporter, + pNetworkAdapter, + } + } + + describe("Deploy", function () { + it("Successfully deploys contract", async function () { + const { pNetworkHeaderReporter, pToken, headerStorage } = await setup() + expect(await pNetworkHeaderReporter.deployed()) + expect(await pNetworkHeaderReporter.TOKEN()).to.equal(pToken.address) + expect(await pNetworkHeaderReporter.VAULT()).to.equal(ZERO_ADDRESS) + expect(await pNetworkHeaderReporter.HEADER_STORAGE()).to.equal(headerStorage.address) + }) + }) + + describe("reportHeaders()", function () { + it("Reports headers over pNetwork", async function () { + const { pNetworkHeaderReporter, pNetworkAdapter, pToken, headerStorage } = await setup() + const blockIds = [0, 1] + const blocks = await Promise.all(blockIds.map((_id) => ethers.provider._getBlock(_id))) + const hashes = blocks.map((_block) => _block.hash) + const expectedUserData = new ethers.utils.AbiCoder().encode(["uint256[]", "bytes32[]"], [blockIds, hashes]) + await expect(pNetworkHeaderReporter.reportHeaders(blockIds, pNetworkAdapter.address)) + .to.emit(pNetworkHeaderReporter, "HeaderReported") + .withArgs(pNetworkHeaderReporter.address, blockIds[0], hashes[0]) + .and.to.emit(pNetworkHeaderReporter, "HeaderReported") + .withArgs(pNetworkHeaderReporter.address, blockIds[1], hashes[1]) + .and.to.emit(pToken, "Redeem") + .withArgs( + pNetworkHeaderReporter.address, + 1, + pNetworkAdapter.address.replace("0x", "").toLowerCase(), + expectedUserData, + "0x87654321", + "0x005fe7f9", + ) + expect(await headerStorage.headers(blockIds[0])).to.equal(hashes[0]) + expect(await headerStorage.headers(blockIds[1])).to.equal(hashes[1]) + }) + }) + }) +}) diff --git a/packages/evm/test/adapters/PNetwork/02_PNetworkAdapter.spec.ts b/packages/evm/test/adapters/PNetwork/03_PNetworkAdapter.spec.ts similarity index 100% rename from packages/evm/test/adapters/PNetwork/02_PNetworkAdapter.spec.ts rename to packages/evm/test/adapters/PNetwork/03_PNetworkAdapter.spec.ts From 0dd9059be5b7cfd199ba09e2c4dff5d9d9388017 Mon Sep 17 00:00:00 2001 From: Alain Olivier Date: Thu, 14 Dec 2023 15:33:55 +0100 Subject: [PATCH 18/19] test(evm): update solidity version for pNetwork mocks --- packages/evm/contracts/adapters/PNetwork/test/ERC777Token.sol | 2 +- packages/evm/contracts/adapters/PNetwork/test/MockVault.sol | 2 +- packages/evm/contracts/adapters/PNetwork/test/PToken.sol | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/evm/contracts/adapters/PNetwork/test/ERC777Token.sol b/packages/evm/contracts/adapters/PNetwork/test/ERC777Token.sol index 08563ba0..41b106b8 100644 --- a/packages/evm/contracts/adapters/PNetwork/test/ERC777Token.sol +++ b/packages/evm/contracts/adapters/PNetwork/test/ERC777Token.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; +pragma solidity ^0.8.17; import "@openzeppelin/contracts/token/ERC777/ERC777.sol"; diff --git a/packages/evm/contracts/adapters/PNetwork/test/MockVault.sol b/packages/evm/contracts/adapters/PNetwork/test/MockVault.sol index 0564c291..6917781f 100644 --- a/packages/evm/contracts/adapters/PNetwork/test/MockVault.sol +++ b/packages/evm/contracts/adapters/PNetwork/test/MockVault.sol @@ -4,7 +4,7 @@ // logic to handle ETHPNT<->PNT fungibility, as well as custom logic to handle GALA tokens after // they upgraded from v1 to v2. -pragma solidity ^0.8.0; +pragma solidity ^0.8.17; import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; diff --git a/packages/evm/contracts/adapters/PNetwork/test/PToken.sol b/packages/evm/contracts/adapters/PNetwork/test/PToken.sol index 5b4df604..3a069a3c 100644 --- a/packages/evm/contracts/adapters/PNetwork/test/PToken.sol +++ b/packages/evm/contracts/adapters/PNetwork/test/PToken.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; +pragma solidity ^0.8.17; import "@openzeppelin/contracts/token/ERC777/ERC777.sol"; From f10894b888188b1e4751139c9738e195d0cdfc91 Mon Sep 17 00:00:00 2001 From: Alain Olivier Date: Thu, 14 Dec 2023 15:34:33 +0100 Subject: [PATCH 19/19] chore(evm): fix deploy script for PNetworkAdapter --- packages/evm/tasks/deploy/adapters/pnetwork.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/evm/tasks/deploy/adapters/pnetwork.ts b/packages/evm/tasks/deploy/adapters/pnetwork.ts index 18d8e7bb..22abfe39 100644 --- a/packages/evm/tasks/deploy/adapters/pnetwork.ts +++ b/packages/evm/tasks/deploy/adapters/pnetwork.ts @@ -79,7 +79,7 @@ task("deploy:adapter:PNetworkAdapter") console.log("Deploying PNetworkAdapter...") const signers: SignerWithAddress[] = await hre.ethers.getSigners() const pNetworkAdapterFactory: PNetworkAdapter__factory = ( - await hre.ethers.getContractFactory("TelepathyAdapter") + await hre.ethers.getContractFactory("PNetworkAdapter") ) const constructorArguments = [ taskArguments.chainId,