Skip to content

Commit

Permalink
Merge branch 'main' into ron/transact-from-eth-to-sub
Browse files Browse the repository at this point in the history
  • Loading branch information
yrong committed Apr 11, 2024
2 parents 68a567f + 61f9f6d commit 923187e
Show file tree
Hide file tree
Showing 103 changed files with 18,767 additions and 3,954 deletions.
61 changes: 61 additions & 0 deletions .github/workflows/npm-publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
name: npm-publish

on:
release:
types: [published]

env:
NPM_AUTH_TOKEN: ${{ secrets.NPM_AUTH_TOKEN }}

jobs:
publish:
runs-on: snowbridge-runner
timeout-minutes: 15
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 2

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

- name: Build Contracts
working-directory: contracts
run: |
forge build
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version: '20.x'
registry-url: 'https://registry.npmjs.org'

- name: Install pnpm
uses: pnpm/action-setup@v3
with:
version: 8

- name: Build Contract Types
working-directory: web/packages/contract-types
run: |
pnpm install
pnpm typechain
pnpm build
- name: Build API
working-directory: web/packages/api
run: |
pnpm install
pnpm build
- name: Publish Contract Types
working-directory: web/packages/contract-types
run: |
pnpm publish
- name: Publish API
working-directory: web/packages/api
run: |
pnpm publish
39 changes: 39 additions & 0 deletions .github/workflows/relayer.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
name: relayer

on:
push:
paths:
- "relayer/**"
branches:
- main
pull_request:
paths:
- "relayer/**"

jobs:
build:
runs-on: snowbridge-runner
timeout-minutes: 15
steps:
- uses: actions/checkout@v1
with:
fetch-depth: 2

- name: setup go
uses: actions/checkout@v4
with:
go-version: '^1.20.1'

- name: check go version
run: go version

- name: install dependencies
working-directory: relayer
run: go mod download

- name: Add gopath to bin
run: echo "$HOME/go/bin" >> $GITHUB_PATH

- name: test
working-directory: relayer
run: go test -v ./...
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ parachain/build_rs_cov.profraw
compiler_config.json
contracts/beefy-state.json

# beacon states generate by relayer
states/

go/
gocache/
go.work*
Expand Down
3 changes: 2 additions & 1 deletion contracts/src/DeployScript.sol
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,8 @@ contract DeployScript is Script {
assetHubAgentID: assetHubAgentID,
assetHubCreateAssetFee: uint128(vm.envUint("CREATE_ASSET_FEE")),
assetHubReserveTransferFee: uint128(vm.envUint("RESERVE_TRANSFER_FEE")),
exchangeRate: ud60x18(vm.envUint("EXCHANGE_RATE"))
exchangeRate: ud60x18(vm.envUint("EXCHANGE_RATE")),
multiplier: ud60x18(vm.envUint("FEE_MULTIPLIER"))
});

GatewayProxy gateway = new GatewayProxy(address(gatewayLogic), abi.encode(config));
Expand Down
29 changes: 18 additions & 11 deletions contracts/src/Gateway.sol
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,10 @@ contract Gateway is IGateway, IInitializable {
using Address for address;
using SafeNativeTransfer for address payable;

address internal immutable AGENT_EXECUTOR;
address public immutable AGENT_EXECUTOR;

// Verification state
address internal immutable BEEFY_CLIENT;
address public immutable BEEFY_CLIENT;

// BridgeHub
ParaID internal immutable BRIDGE_HUB_PARA_ID;
Expand Down Expand Up @@ -393,6 +393,7 @@ contract Gateway is IGateway, IInitializable {
SetPricingParametersParams memory params = abi.decode(data, (SetPricingParametersParams));
pricing.exchangeRate = params.exchangeRate;
pricing.deliveryCost = params.deliveryCost;
pricing.multiplier = params.multiplier;
emit PricingParametersChanged();
}

Expand Down Expand Up @@ -465,19 +466,22 @@ contract Gateway is IGateway, IInitializable {
}

// Convert foreign currency to native currency (ROC/KSM/DOT -> ETH)
function _convertToNative(UD60x18 exchangeRate, uint256 amount) internal view returns (uint256) {
UD60x18 amountFP = convert(amount);
function _convertToNative(UD60x18 exchangeRate, UD60x18 multiplier, UD60x18 amount)
internal
view
returns (uint256)
{
UD60x18 ethDecimals = convert(1e18);
UD60x18 foreignDecimals = convert(10).pow(convert(uint256(FOREIGN_TOKEN_DECIMALS)));
UD60x18 nativeAmountFP = amountFP.mul(exchangeRate).div(foreignDecimals).mul(ethDecimals);
uint256 nativeAmount = convert(nativeAmountFP);
return nativeAmount;
UD60x18 nativeAmount = multiplier.mul(amount).mul(exchangeRate).div(foreignDecimals).mul(ethDecimals);
return convert(nativeAmount);
}

// Calculate the fee for accepting an outbound message
function _calculateFee(Costs memory costs) internal view returns (uint256) {
PricingStorage.Layout storage pricing = PricingStorage.layout();
return costs.native + _convertToNative(pricing.exchangeRate, pricing.deliveryCost + costs.foreign);
UD60x18 amount = convert(pricing.deliveryCost + costs.foreign);
return costs.native + _convertToNative(pricing.exchangeRate, pricing.multiplier, amount);
}

// Submit an outbound message to Polkadot, after taking fees
Expand Down Expand Up @@ -572,24 +576,26 @@ contract Gateway is IGateway, IInitializable {
uint128 assetHubReserveTransferFee;
/// @dev extra fee to discourage spamming
uint256 registerTokenFee;
/// @dev Fee multiplier
UD60x18 multiplier;
}

/// @dev Initialize storage in the gateway
/// NOTE: This is not externally accessible as this function selector is overshadowed in the proxy
function initialize(bytes calldata data) external {
function initialize(bytes calldata data) external virtual {
// Prevent initialization of storage in implementation contract
if (ERC1967.load() == address(0)) {
revert Unauthorized();
}

Config memory config = abi.decode(data, (Config));

CoreStorage.Layout storage core = CoreStorage.layout();

if (core.channels[PRIMARY_GOVERNANCE_CHANNEL_ID].agent != address(0)) {
revert AlreadyInitialized();
}

Config memory config = abi.decode(data, (Config));

core.mode = config.mode;

// Initialize agent for BridgeHub
Expand All @@ -616,6 +622,7 @@ contract Gateway is IGateway, IInitializable {
PricingStorage.Layout storage pricing = PricingStorage.layout();
pricing.exchangeRate = config.exchangeRate;
pricing.deliveryCost = config.deliveryCost;
pricing.multiplier = config.multiplier;

// Initialize assets storage
AssetsStorage.Layout storage assets = AssetsStorage.layout();
Expand Down
2 changes: 2 additions & 0 deletions contracts/src/Params.sol
Original file line number Diff line number Diff line change
Expand Up @@ -79,4 +79,6 @@ struct SetPricingParametersParams {
UD60x18 exchangeRate;
/// @dev The cost of delivering messages to BridgeHub in DOT
uint128 deliveryCost;
/// @dev Fee multiplier
UD60x18 multiplier;
}
2 changes: 2 additions & 0 deletions contracts/src/storage/PricingStorage.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ library PricingStorage {
UD60x18 exchangeRate;
/// @dev The cost of delivering messages to BridgeHub in DOT
uint128 deliveryCost;
/// @dev Fee multiplier
UD60x18 multiplier;
}

bytes32 internal constant SLOT = keccak256("org.snowbridge.storage.pricing");
Expand Down
33 changes: 33 additions & 0 deletions contracts/src/upgrades/rococo/GatewayV2.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2023 Snowfork <[email protected]>
pragma solidity 0.8.23;

import "../../Gateway.sol";

import {UD60x18, convert} from "prb/math/src/UD60x18.sol";
import {PricingStorage} from "../../storage/PricingStorage.sol";

contract GatewayV2 is Gateway {
constructor(
address beefyClient,
address agentExecutor,
ParaID bridgeHubParaID,
bytes32 bridgeHubAgentID,
uint8 foreignTokenDecimals
) Gateway(beefyClient, agentExecutor, bridgeHubParaID, bridgeHubAgentID, foreignTokenDecimals) {}

function initialize(bytes memory data) external override {
// Prevent initialization of storage in implementation contract
if (ERC1967.load() == address(0)) {
revert Unauthorized();
}

PricingStorage.Layout storage pricing = PricingStorage.layout();

if (pricing.multiplier != convert(0)) {
revert AlreadyInitialized();
}

pricing.multiplier = abi.decode(data, (UD60x18));
}
}
24 changes: 16 additions & 8 deletions contracts/test/Gateway.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ contract GatewayTest is Test {

// ETH/DOT exchange rate
UD60x18 public exchangeRate = ud60x18(0.0025e18);
UD60x18 public multiplier = ud60x18(1e18);

function setUp() public {
AgentExecutor executor = new AgentExecutor();
Expand All @@ -108,7 +109,8 @@ contract GatewayTest is Test {
assetHubAgentID: assetHubAgentID,
assetHubCreateAssetFee: createTokenFee,
assetHubReserveTransferFee: sendTokenFee,
exchangeRate: exchangeRate
exchangeRate: exchangeRate,
multiplier: multiplier
});
gateway = new GatewayProxy(address(gatewayLogic), abi.encode(config));
GatewayMock(address(gateway)).setCommitmentsAreVerified(true);
Expand Down Expand Up @@ -496,7 +498,7 @@ contract GatewayTest is Test {
assertEq(GatewayV2(address(gateway)).getValue(), 42);
}

function testUgradeInitializerRunsOnlyOnce() public {
function testUpgradeInitializerRunsOnlyOnce() public {
// Upgrade to this current logic contract
AgentExecutor executor = new AgentExecutor();
GatewayMock currentLogic =
Expand All @@ -510,7 +512,8 @@ contract GatewayTest is Test {
assetHubAgentID: assetHubAgentID,
assetHubCreateAssetFee: createTokenFee,
assetHubReserveTransferFee: sendTokenFee,
exchangeRate: exchangeRate
exchangeRate: exchangeRate,
multiplier: multiplier
});

UpgradeParams memory params = UpgradeParams({
Expand All @@ -529,7 +532,7 @@ contract GatewayTest is Test {

testSetPricingParameters();
uint256 fee = IGateway(address(gateway)).quoteRegisterTokenFee();
assertEq(fee, 10000000000000000);
assertEq(fee, 20000000000000001);

testCreateAgent();
assertNotEq(GatewayMock(address(gateway)).agentOf(agentID), address(0));
Expand All @@ -551,7 +554,7 @@ contract GatewayTest is Test {

// Verify that storage was not overwritten
fee = IGateway(address(gateway)).quoteRegisterTokenFee();
assertEq(fee, 10000000000000000);
assertEq(fee, 20000000000000001);
assertNotEq(GatewayMock(address(gateway)).agentOf(agentID), address(0));
}

Expand Down Expand Up @@ -888,14 +891,19 @@ contract GatewayTest is Test {
function testSetPricingParameters() public {
uint256 fee = IGateway(address(gateway)).quoteRegisterTokenFee();
assertEq(fee, 5000000000000000);
// Double the exchangeRate
// Double both the exchangeRate and multiplier. Should lead to an 4x fee increase
GatewayMock(address(gateway)).setPricingParametersPublic(
abi.encode(
SetPricingParametersParams({exchangeRate: exchangeRate.mul(convert(2)), deliveryCost: outboundFee})
SetPricingParametersParams({
exchangeRate: exchangeRate.mul(convert(2)),
multiplier: multiplier.mul(convert(2)),
deliveryCost: outboundFee
})
)
);
// Should expect 4x fee increase
fee = IGateway(address(gateway)).quoteRegisterTokenFee();
assertEq(fee, 10000000000000000);
assertEq(fee, 20000000000000001);
}

function testSendTokenToForeignDestWithInvalidFee() public {
Expand Down
2 changes: 2 additions & 0 deletions docs/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,5 @@
* [Initialize Ethereum light client with Forced Checkpoint](runbooks/initialize-ethereum-light-client-with-forced-checkpoint.md)
* [Initial Deployment of Gateway Contracts](runbooks/initial-deployment-of-gateway-contracts.md)
* [Halt Bridge in Case of Emergency](runbooks/halt-bridge-in-case-of-emergency.md)
* [Contract Upgrades](runbooks/contract-upgrades.md)
* [Run Relayers](runbooks/run-relayers.md)
Loading

0 comments on commit 923187e

Please sign in to comment.