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

Add receive fn to HypNative and Scaled variant #2671

Merged
merged 22 commits into from
Sep 12, 2023
Merged
Show file tree
Hide file tree
Changes from 14 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
12 changes: 6 additions & 6 deletions .github/workflows/node.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ jobs:
with:
node-version: 18

- name: Install Foundry
uses: onbjerg/foundry-toolchain@v1

- name: yarn-cache
uses: actions/cache@v3
with:
Expand Down Expand Up @@ -99,6 +102,9 @@ jobs:
with:
submodules: recursive

- name: Install Foundry
uses: onbjerg/foundry-toolchain@v1

- uses: actions/cache@v3
with:
path: ./*
Expand Down Expand Up @@ -162,12 +168,6 @@ jobs:
- name: Install Foundry
uses: onbjerg/foundry-toolchain@v1

- name: Install dependencies
run: cd solidity && forge install

- name: Forge build
run: cd solidity && forge build --build-info

#- name: gas
# run: yarn workspace @hyperlane-xyz/core run gas-ci

Expand Down
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
[submodule "solidity/lib/forge-std"]
path = solidity/lib/forge-std
url = https://github.com/foundry-rs/forge-std
[submodule "typescript/token/lib/forge-std"]
path = typescript/token/lib/forge-std
url = https://github.com/foundry-rs/forge-std
25 changes: 15 additions & 10 deletions typescript/token/contracts/HypNative.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,13 @@
* @dev Supply on each chain is not constant but the aggregate supply across all chains is.
*/
contract HypNative is TokenRouter {
/**
* @dev Emitted when native tokens are donated to the contract.
* @param sender The address of the sender.
* @param amount The amount of native tokens donated.
*/
event Donation(address indexed sender, uint256 amount);

/**
* @notice Initializes the Hyperlane router, ERC20 metadata, and mints initial supply to deployer.
* @param _mailbox The address of the mailbox contract.
Expand All @@ -35,26 +42,20 @@
uint32 _destination,
bytes32 _recipient,
uint256 _amount
) public payable override returns (bytes32 messageId) {
) public payable virtual override returns (bytes32 messageId) {
require(msg.value >= _amount, "Native: amount exceeds msg.value");
uint256 gasPayment = msg.value - _amount;
messageId = _dispatchWithGas(
_destination,
Message.format(_recipient, _amount, ""),
gasPayment,
msg.sender
);
emit SentTransferRemote(_destination, _recipient, _amount);
return _transferRemote(_destination, _recipient, _amount, gasPayment);
}

function balanceOf(address _account) external view returns (uint256) {
return _account.balance;
}

/**
* @inheritdoc TokenRouter
* @dev No-op because native amount is transferred in `msg.value`
* @dev Compiler will not include this in the bytecode.
* @inheritdoc TokenRouter
*/
function _transferFromSender(uint256)
internal
Expand All @@ -73,7 +74,11 @@
address _recipient,
uint256 _amount,
bytes calldata // no metadata
) internal override {
) internal virtual override {
Address.sendValue(payable(_recipient), _amount);
}

receive() external payable {
emit Donation(msg.sender, msg.value);

Check warning on line 82 in typescript/token/contracts/HypNative.sol

View check run for this annotation

Codecov / codecov/patch

typescript/token/contracts/HypNative.sol#L82

Added line #L82 was not covered by tests
}
}
48 changes: 48 additions & 0 deletions typescript/token/contracts/extensions/HypNativeScaled.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity >=0.8.0;

import {HypNative} from "../HypNative.sol";
import {TokenRouter} from "../libs/TokenRouter.sol";

/**
* @title Hyperlane Native Token that scales native value by a fixed factor for consistency with other tokens.
yorhodes marked this conversation as resolved.
Show resolved Hide resolved
* @dev The scale factor multiplies the `message.amount` to the local native token amount.
* Conversely, it divides the local native `msg.value` amount by `scale` to encode the `message.amount`.
* @author Abacus Works
*/
contract HypNativeScaled is HypNative {
uint256 public immutable scale;

constructor(uint256 _scale) {
scale = _scale;

Check warning on line 17 in typescript/token/contracts/extensions/HypNativeScaled.sol

View check run for this annotation

Codecov / codecov/patch

typescript/token/contracts/extensions/HypNativeScaled.sol#L16-L17

Added lines #L16 - L17 were not covered by tests
}

/**
* @inheritdoc HypNative
* @dev Sends scaled `msg.value` (divided by `scale`) to `_recipient`.
*/
function transferRemote(

Check warning on line 24 in typescript/token/contracts/extensions/HypNativeScaled.sol

View check run for this annotation

Codecov / codecov/patch

typescript/token/contracts/extensions/HypNativeScaled.sol#L24

Added line #L24 was not covered by tests
uint32 _destination,
bytes32 _recipient,
uint256 _amount
) public payable override returns (bytes32 messageId) {
require(msg.value >= _amount, "Native: amount exceeds msg.value");
uint256 gasPayment = msg.value - _amount;
uint256 scaledAmount = _amount / scale;
return

Check warning on line 32 in typescript/token/contracts/extensions/HypNativeScaled.sol

View check run for this annotation

Codecov / codecov/patch

typescript/token/contracts/extensions/HypNativeScaled.sol#L30-L32

Added lines #L30 - L32 were not covered by tests
_transferRemote(_destination, _recipient, scaledAmount, gasPayment);
}

/**
* @dev Sends scaled `_amount` (multipled by `scale`) to `_recipient`.
* @inheritdoc TokenRouter
*/
function _transferTo(

Check warning on line 40 in typescript/token/contracts/extensions/HypNativeScaled.sol

View check run for this annotation

Codecov / codecov/patch

typescript/token/contracts/extensions/HypNativeScaled.sol#L40

Added line #L40 was not covered by tests
address _recipient,
uint256 _amount,
bytes calldata metadata // no metadata
) internal override {
uint256 scaledAmount = _amount * scale;
HypNative._transferTo(_recipient, scaledAmount, metadata);

Check warning on line 46 in typescript/token/contracts/extensions/HypNativeScaled.sol

View check run for this annotation

Codecov / codecov/patch

typescript/token/contracts/extensions/HypNativeScaled.sol#L45-L46

Added lines #L45 - L46 were not covered by tests
}
}
24 changes: 22 additions & 2 deletions typescript/token/contracts/libs/TokenRouter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,32 @@ abstract contract TokenRouter is GasRouter {
uint32 _destination,
bytes32 _recipient,
uint256 _amountOrId
) public payable virtual returns (bytes32 messageId) {
) external payable virtual returns (bytes32 messageId) {
return
_transferRemote(_destination, _recipient, _amountOrId, msg.value);
}

/**
* @notice Transfers `_amountOrId` token to `_recipient` on `_destination` domain.
* @dev Delegates transfer logic to `_transferFromSender` implementation.
* @dev Emits `SentTransferRemote` event on the origin chain.
* @param _destination The identifier of the destination chain.
* @param _recipient The address of the recipient on the destination chain.
* @param _amountOrId The amount or identifier of tokens to be sent to the remote recipient.
* @param _gasPayment The amount of native token to pay for interchain gas.
* @return messageId The identifier of the dispatched message.
*/
function _transferRemote(
uint32 _destination,
bytes32 _recipient,
uint256 _amountOrId,
uint256 _gasPayment
) internal returns (bytes32 messageId) {
bytes memory metadata = _transferFromSender(_amountOrId);
messageId = _dispatchWithGas(
_destination,
Message.format(_recipient, _amountOrId, metadata),
msg.value, // interchain gas payment
_gasPayment,
msg.sender // refund address
);
emit SentTransferRemote(_destination, _recipient, _amountOrId);
Expand Down
10 changes: 9 additions & 1 deletion typescript/token/contracts/test/ERC20Test.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,19 @@ pragma solidity >=0.8.0;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

contract ERC20Test is ERC20 {
uint8 public immutable _decimals;

constructor(
string memory name,
string memory symbol,
uint256 totalSupply
uint256 totalSupply,
uint8 __decimals
) ERC20(name, symbol) {
_decimals = __decimals;
_mint(msg.sender, totalSupply);
}

function decimals() public view override returns (uint8) {
return _decimals;
}
}
3 changes: 3 additions & 0 deletions typescript/token/foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,8 @@ src = "contracts"
out = "out"
libs = ["lib"]
allow_paths = ["../../node_modules", "../../solidity"]
solc = '0.8.15'
optimizer = true
optimizer_runs = 999_999

# See more config options https://github.com/foundry-rs/foundry/tree/master/config
1 change: 1 addition & 0 deletions typescript/token/lib/forge-std
Submodule forge-std added at 74cfb7
15 changes: 10 additions & 5 deletions typescript/token/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,26 +16,29 @@ export type TokenMetadata = {
totalSupply: ethers.BigNumberish;
};

export type ERC20Metadata = TokenMetadata & {
export type TokenDecimals = {
decimals: number;
scale?: number;
};

export type ERC20Metadata = TokenMetadata & TokenDecimals;

export const isTokenMetadata = (metadata: any): metadata is TokenMetadata =>
metadata.name && metadata.symbol && metadata.totalSupply !== undefined; // totalSupply can be 0

export const isErc20Metadata = (metadata: any): metadata is ERC20Metadata =>
metadata.decimals && isTokenMetadata(metadata);

export type SyntheticConfig = TokenMetadata & {
export type SyntheticConfig = {
type: TokenType.synthetic | TokenType.syntheticUri;
};
} & TokenMetadata;
export type CollateralConfig = {
type: TokenType.collateral | TokenType.collateralUri;
token: string;
} & Partial<ERC20Metadata>;
export type NativeConfig = {
type: TokenType.native;
};
} & Partial<TokenDecimals>;

export type TokenConfig = SyntheticConfig | CollateralConfig | NativeConfig;

Expand All @@ -58,7 +61,9 @@ export const isUriConfig = (config: TokenConfig) =>
config.type === TokenType.collateralUri;

export type HypERC20Config = GasRouterConfig & SyntheticConfig & ERC20Metadata;
export type HypERC20CollateralConfig = GasRouterConfig & CollateralConfig;
export type HypERC20CollateralConfig = GasRouterConfig &
CollateralConfig &
Partial<ERC20Metadata>;
export type HypNativeConfig = GasRouterConfig & NativeConfig;
export type ERC20RouterConfig =
| HypERC20Config
Expand Down
23 changes: 17 additions & 6 deletions typescript/token/src/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ import {
HypERC721URIStorage__factory,
HypERC721__factory,
HypNative,
HypNativeScaled__factory,
HypNative__factory,
} from './types';

Expand Down Expand Up @@ -140,12 +141,22 @@ export class HypERC20Deployer extends GasRouterDeployer<
chain: ChainName,
config: HypNativeConfig,
): Promise<HypNative> {
const router = await this.deployContractFromFactory(
chain,
new HypNative__factory(),
'HypNative',
[],
);
let router: HypNative;
if (config.scale) {
router = await this.deployContractFromFactory(
chain,
new HypNativeScaled__factory(),
'HypNativeScaled',
[config.scale],
);
} else {
router = await this.deployContractFromFactory(
chain,
new HypNative__factory(),
'HypNative',
[],
);
}
await this.multiProvider.handleTx(
chain,
router.initialize(config.mailbox, config.interchainGasPaymaster),
Expand Down
Loading
Loading