Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

added Router Protocol adapter to the Hashi's pool of adapters #49

Merged
merged 3 commits into from
Mar 26, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 60 additions & 0 deletions packages/evm/contracts/adapters/Router/RouterAdapter.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity ^0.8.20;

import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import { IDapp } from "@routerprotocol/evm-gateway-contracts/contracts/IDapp.sol";
import { IGateway } from "@routerprotocol/evm-gateway-contracts/contracts/IGateway.sol";
import { BlockHashAdapter } from "../BlockHashAdapter.sol";

contract RouterAdapter is BlockHashAdapter, Ownable, IDapp {
string public constant PROVIDER = "router";

IGateway public immutable ROUTER_GATEWAY;

mapping(bytes32 => bytes32) public enabledReporters;
mapping(bytes32 => uint256) public chainIds;

error UnauthorizedRouterReceive();
error RouterIAckNotSupported();

event ReporterSet(uint256 indexed chainId, string name, string indexed reporter);

constructor(address routerGateway) {
ROUTER_GATEWAY = IGateway(routerGateway);
}

function setReporterByChain(
uint256 chainId,
string calldata chainIdStr,
string calldata reporter
) external onlyOwner {
bytes32 chainIdHash = keccak256(bytes(chainIdStr));
enabledReporters[chainIdHash] = keccak256(bytes(reporter));
chainIds[chainIdHash] = chainId;
emit ReporterSet(chainId, chainIdStr, reporter);
}

function iReceive(
string calldata requestSender,
bytes calldata packet,
string calldata srcChainId
) external override returns (bytes memory) {
bytes32 chainIdHash = keccak256(bytes(srcChainId));
uint256 sourceChainId = chainIds[chainIdHash];

if (
msg.sender != address(ROUTER_GATEWAY) ||
enabledReporters[chainIdHash] != keccak256(bytes(requestSender)) ||
sourceChainId == 0
) revert UnauthorizedRouterReceive();

(uint256[] memory ids, bytes32[] memory hashes) = abi.decode(packet, (uint256[], bytes32[]));
_storeHashes(sourceChainId, ids, hashes);

return hex"";
}

function iAck(uint256, bool, bytes memory) external override {
Copy link
Collaborator

Choose a reason for hiding this comment

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

This fx can be restricted to pure

revert RouterIAckNotSupported();
}
}
119 changes: 119 additions & 0 deletions packages/evm/contracts/adapters/Router/RouterReporter.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity ^0.8.20;

import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import { Strings } from "@openzeppelin/contracts/utils/Strings.sol";
import { Reporter } from "../Reporter.sol";
import { IGateway } from "@routerprotocol/evm-gateway-contracts/contracts/IGateway.sol";
import { Strings } from "@openzeppelin/contracts/utils/Strings.sol";

interface IRouterGateway is IGateway {
function iSendDefaultFee() external view returns (uint256);

function currentVersion() external view returns (uint256);
}

interface IRouterGasStation {
function payFee(string memory destChainId, uint256 destGasLimit) external payable returns (uint256);
function getNativeFees(
string memory destChainId,
uint256 destGasLimit
) external view returns (uint256) ;
}

contract RouterReporter is Reporter, Ownable {
using Strings for uint256;

string public constant PROVIDER = "router";
bytes32 private constant NULL_STRING = keccak256("");
IRouterGateway public immutable ROUTER_GATEWAY;
IRouterGasStation public immutable ROUTER_GAS_STATION;
uint256 public immutable CURRENT_GATEWAY_VERSION;
string public feePayer;

mapping(uint256 => string) public chainIds;

error ChainIdNotSupported(uint256 chainId);
error InsufficientFeePassed();

event ChainIdSet(uint256 indexed chainId, string indexed chainIdString);
event FeePayerSet(string oldFeePayer, string feePayer);

constructor(
address headerStorage,
address yaho,
address routerGateway,
address routerGasStation,
string memory routerFeePayer
) Reporter(headerStorage, yaho) {
ROUTER_GATEWAY = IRouterGateway(routerGateway);
ROUTER_GAS_STATION = IRouterGasStation(routerGasStation);

feePayer = routerFeePayer;

CURRENT_GATEWAY_VERSION = ROUTER_GATEWAY.currentVersion();
ROUTER_GATEWAY.setDappMetadata(routerFeePayer);
}

function setRouterFeePayer(string memory routerFeePayer) external onlyOwner {
string memory oldFeePayer = feePayer;
feePayer = routerFeePayer;
ROUTER_GATEWAY.setDappMetadata(routerFeePayer);

emit FeePayerSet(oldFeePayer, routerFeePayer);
}

function setChainIdStringByChainId(uint256 chainId, string calldata chainIdString) external onlyOwner {
chainIds[chainId] = chainIdString;
emit ChainIdSet(chainId, chainIdString);
}

function getRequestMetadata() internal pure returns (bytes memory) {
bytes memory requestMetadata = abi.encodePacked(
uint64(200_000),
uint64(0),
uint64(0),
uint64(0),
uint128(0),
uint8(0),
false,
string("")
);
return requestMetadata;
}

function _dispatch(
uint256 targetChainId,
address adapter,
uint256[] memory ids,
bytes32[] memory hashes
) internal override returns (bytes32) {
string memory targetChainIdStr = chainIds[targetChainId];
if (keccak256(abi.encode(targetChainIdStr)) == NULL_STRING) revert ChainIdNotSupported(targetChainId);

bytes memory payload = abi.encode(ids, hashes);
string memory stringAdapter = uint256(uint160(adapter)).toHexString(20);
bytes memory requestPacket = abi.encode(stringAdapter, payload);
bytes memory requestMetadata = getRequestMetadata();

uint256 iSendFee = ROUTER_GATEWAY.iSendDefaultFee();
uint256 crosstalkFee = ROUTER_GAS_STATION.getNativeFees(targetChainIdStr, 200_000);

if (address(this).balance < (iSendFee + crosstalkFee)) revert InsufficientFeePassed();

ROUTER_GAS_STATION.payFee{ value: crosstalkFee }(targetChainIdStr, 200_000);

ROUTER_GATEWAY.iSend{ value: iSendFee }(
CURRENT_GATEWAY_VERSION,
0,
string(""),
targetChainIdStr,
requestMetadata,
requestPacket
);

return bytes32(0);
}

receive() external payable {}
}
1 change: 1 addition & 0 deletions packages/evm/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@
"@hyperlane-xyz/core": "^3.1.10",
"@openzeppelin/contracts-upgradeable": "^4.8.1",
"@polytope-labs/solidity-merkle-trees": "^0.2.1",
"@routerprotocol/evm-gateway-contracts": "^1.1.13",
"hardhat-change-network": "^0.0.7",
"hardhat-deploy": "^0.11.31",
"openzeppelin": "npm:@openzeppelin/[email protected]",
Expand Down
1 change: 1 addition & 0 deletions packages/evm/tasks/deploy/adapters/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@ import "./sygma"
import "./telepathy"
import "./wormhole"
import "./zetachain"
import "./router"
60 changes: 60 additions & 0 deletions packages/evm/tasks/deploy/adapters/router.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import type { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"
import { task, types } from "hardhat/config"
import type { TaskArguments } from "hardhat/types"
import type { RouterAdapter } from "../../../types/contracts/adapters/Router/RouterAdapter.sol/RouterAdapter"
import type { RouterAdapter__factory } from "../../../types/factories/contracts/adapters/Router/RouterAdapter.sol/RouterAdapter__factory"
import type { RouterReporter } from "../../../types/contracts/adapters/Router/RouterReporter.sol/RouterReporter"
import type { RouterReporter__factory } from "../../../types/factories/contracts/adapters/Router/RouterReporter.sol/RouterReporter__factory"
import { verify } from "../index"

task("deploy:adapter:RouterAdapter")
.addParam("routerGateway", "address of the Router gateway contract")
.addFlag("verify", "whether to verify the contract on Etherscan")
.setAction(async function (taskArguments: TaskArguments, hre) {
console.log("Deploying RouterAdapter...")
const signers: SignerWithAddress[] = await hre.ethers.getSigners()
const routerAdapterFactory: RouterAdapter__factory = <RouterAdapter__factory>(
await hre.ethers.getContractFactory("RouterAdapter")
)
const constructorArguments = [
taskArguments.routerGateway,
] as const
const routerAdapter: RouterAdapter = <RouterAdapter>(
await routerAdapterFactory.connect(signers[0]).deploy(...constructorArguments)
)
await routerAdapter.deployed()
console.log("RouterAdapter deployed to:", routerAdapter.address)
if (taskArguments.verify) await verify(hre, routerAdapter, constructorArguments)
})

task("deploy:adapter:RouterReporter")
.addParam("headerStorage", "address of the header storage contract")
.addParam("yaho", "address of the Yaho contract", undefined, types.string)
.addParam("routerGateway", "address of the Router gateway contract")
.addParam("routerGasStation", "address of the Router gas station contract")
.addParam(
"routerFeePayer",
"address of the fee payer for this contract (https://docs.routerprotocol.com/develop/message-transfer-via-crosstalk/evm-guides/iDapp-functions/setDappMetadata)",
)
.addFlag("verify", "whether to verify the contract on Etherscan")
.setAction(async function (taskArguments: TaskArguments, hre) {
console.log("Deploying RouterReporter...")
const signers: SignerWithAddress[] = await hre.ethers.getSigners()
const routerReporterFactory: RouterReporter__factory = <RouterReporter__factory>(
await hre.ethers.getContractFactory("RouterReporter")
)
const constructorArguments = [
taskArguments.headerStorage,
taskArguments.yaho,
taskArguments.routerGateway,
taskArguments.routerGasStation,
taskArguments.routerFeePayer,
] as const
const routerReporter: RouterReporter = <RouterReporter>(
await routerReporterFactory.connect(signers[0]).deploy(...constructorArguments)
)
await routerReporter.deployed()
console.log("RouterReporter deployed to:", routerReporter.address)
if (taskArguments.verify) await verify(hre, routerReporter, constructorArguments)
})

56 changes: 22 additions & 34 deletions packages/evm/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -955,35 +955,6 @@ __metadata:
languageName: node
linkType: hard

"@gnosis.pm/mock-contract@npm:^4.0.0":
version: 4.0.0
resolution: "@gnosis.pm/mock-contract@npm:4.0.0"
checksum: ce6f3d6218779a1899f9697dc668ce85c9fcd8b743a8c7cd157124130cc79ba0b2f64faa1a723a03c41134dc617bec2af0a757a381397674e745d694a8cb4bb2
languageName: node
linkType: hard

"@gnosis.pm/safe-contracts@npm:1.3.0":
version: 1.3.0
resolution: "@gnosis.pm/safe-contracts@npm:1.3.0"
peerDependencies:
ethers: ^5.1.4
checksum: fea331745e3af0ed7322115e5bd4cc37de914253d9f6e1d85a4713e7e1fdc758bdba95cd2c38c6ba2cfd1bb289378d983d40723c3c525331dce0ed30418ab990
languageName: node
linkType: hard

"@gnosis.pm/zodiac@npm:^3.3.2":
version: 3.3.2
resolution: "@gnosis.pm/zodiac@npm:3.3.2"
dependencies:
"@gnosis.pm/mock-contract": ^4.0.0
"@gnosis.pm/safe-contracts": 1.3.0
"@openzeppelin/contracts": ^4.8.1
"@openzeppelin/contracts-upgradeable": ^4.8.1
ethers: ^5.7.1
checksum: 796258749d673b552db0cf231ba3968acd2dfe6dfce759f36e91503c6cf29d26bd87c31f883f1ffc6879272ec041c8a7413756364615072339df562c4f90ac31
languageName: node
linkType: hard

"@gnosis/hashi@workspace:.":
version: 0.0.0-use.local
resolution: "@gnosis/hashi@workspace:."
Expand All @@ -998,7 +969,6 @@ __metadata:
"@ethersproject/bignumber": ^5.7.0
"@ethersproject/bytes": ^5.7.0
"@ethersproject/providers": ^5.7.2
"@gnosis.pm/zodiac": ^3.3.2
"@hyperlane-xyz/core": ^3.1.10
"@nomicfoundation/hardhat-chai-matchers": ^1.0.4
"@nomicfoundation/hardhat-network-helpers": ^1.0.8
Expand All @@ -1007,6 +977,7 @@ __metadata:
"@nomiclabs/hardhat-etherscan": ^3.1.2
"@openzeppelin/contracts-upgradeable": ^4.8.1
"@polytope-labs/solidity-merkle-trees": ^0.2.1
"@routerprotocol/evm-gateway-contracts": ^1.1.13
"@trivago/prettier-plugin-sort-imports": ^4.0.0
"@typechain/ethers-v5": ^10.1.1
"@typechain/hardhat": ^6.1.4
Expand Down Expand Up @@ -1622,6 +1593,13 @@ __metadata:
languageName: node
linkType: hard

"@openzeppelin/contracts-upgradeable@npm:^4.7.3":
version: 4.9.6
resolution: "@openzeppelin/contracts-upgradeable@npm:4.9.6"
checksum: 481075e7222cab025ae55304263fca69a2d04305521957bc16d2aece9fa2b86b6914711724822493e3d04df7e793469cd0bcb1e09f0ddd10cb4e360ac7eed12a
languageName: node
linkType: hard

"@openzeppelin/contracts-upgradeable@npm:^4.8.1":
version: 4.8.2
resolution: "@openzeppelin/contracts-upgradeable@npm:4.8.2"
Expand All @@ -1643,10 +1621,10 @@ __metadata:
languageName: node
linkType: hard

"@openzeppelin/contracts@npm:^4.8.1":
version: 4.8.3
resolution: "@openzeppelin/contracts@npm:4.8.3"
checksum: aea130d38d46840c5cbe3adbaa9a7ac645e4bd66ad3f3baf2fa78588c408d1a686170b3408c9e2e5e05530fba22ecdc00d7efb6b27852a8b29f91accbc0af255
"@openzeppelin/contracts@npm:^4.4.1":
version: 4.9.6
resolution: "@openzeppelin/contracts@npm:4.9.6"
checksum: 274b6e968268294f12d5ca4f0278f6e6357792c8bb4d76664f83dbdc325f780541538a127e6a6e97e4f018088b42f65952014dec9c745c0fa25081f43ef9c4bf
languageName: node
linkType: hard

Expand All @@ -1671,6 +1649,16 @@ __metadata:
languageName: node
linkType: hard

"@routerprotocol/evm-gateway-contracts@npm:^1.1.13":
version: 1.1.13
resolution: "@routerprotocol/evm-gateway-contracts@npm:1.1.13"
dependencies:
"@openzeppelin/contracts": ^4.4.1
"@openzeppelin/contracts-upgradeable": ^4.7.3
checksum: 08898738eaf06d330a96ada9c1760ddc5ee8e3f7871eca425d1ce66a3c1496766d1509f40aec203b6f1dd6f077696fdf5c7e489ba3291f0377261e556d4999cc
languageName: node
linkType: hard

"@scure/base@npm:~1.1.0":
version: 1.1.1
resolution: "@scure/base@npm:1.1.1"
Expand Down