Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
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
25 changes: 16 additions & 9 deletions contracts/src/Gateway.sol
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,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 @@ -462,19 +463,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 @@ -569,24 +573,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 @@ -613,6 +619,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 @@ -90,6 +90,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 @@ -103,7 +104,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 @@ -483,7 +485,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 @@ -497,7 +499,8 @@ contract GatewayTest is Test {
assetHubAgentID: assetHubAgentID,
assetHubCreateAssetFee: createTokenFee,
assetHubReserveTransferFee: sendTokenFee,
exchangeRate: exchangeRate
exchangeRate: exchangeRate,
multiplier: multiplier
});

UpgradeParams memory params = UpgradeParams({
Expand All @@ -516,7 +519,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 @@ -538,7 +541,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 @@ -875,14 +878,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
5 changes: 3 additions & 2 deletions web/packages/test/scripts/set-env.sh
Original file line number Diff line number Diff line change
Expand Up @@ -94,12 +94,13 @@ export REJECT_OUTBOUND_MESSAGES="${REJECT_OUTBOUND_MESSAGES:-false}"

## Fee
export REGISTER_TOKEN_FEE="${REGISTER_TOKEN_FEE:-200000000000000000}"
export DELIVERY_COST="${DELIVERY_COST:-10000000000}"
export CREATE_ASSET_FEE="${CREATE_ASSET_FEE:-10000000000}"
export RESERVE_TRANSFER_FEE="${RESERVE_TRANSFER_FEE:-10000000000}"

## Price
## Pricing Parameters
export EXCHANGE_RATE="${EXCHANGE_RATE:-2500000000000000}"
export DELIVERY_COST="${DELIVERY_COST:-10000000000}"
export FEE_MULTIPLIER="${FEE_MULTIPLIER:-1000000000000000000}"
export FEE_PER_GAS="${FEE_PER_GAS:-20000000000}"

## Reward
Expand Down