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

Fee for Gateway.sendToken should cover all costs #1015

Merged
merged 43 commits into from
Nov 30, 2023
Merged
Show file tree
Hide file tree
Changes from 36 commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
bb91e65
Gateway should provide detailed fee info
vgeddes Nov 21, 2023
5d80c41
Fix tests
vgeddes Nov 22, 2023
ac5c8b1
Improve fee query API
vgeddes Nov 22, 2023
0eb03f2
Add destinationChainFee parameter
vgeddes Nov 23, 2023
26c9098
forge install: prb-math
vgeddes Nov 23, 2023
2fcc415
Add exchage rate to gateway
vgeddes Nov 23, 2023
d88dfd5
Update gateway
vgeddes Nov 26, 2023
2616424
rename Cost to Costs
vgeddes Nov 27, 2023
d0ad0e9
Fix compile error&tests
yrong Nov 27, 2023
5a63319
Merge branch 'main' into remove-redundant-address
yrong Nov 27, 2023
5df3d94
remove obsolete code
vgeddes Nov 27, 2023
510880c
fix
vgeddes Nov 27, 2023
5e88fd4
Fix contract tests
yrong Nov 27, 2023
1817118
Merge branch 'remove-redundant-address' of https://github.com/Snowfor…
yrong Nov 27, 2023
0fb8c7d
Test
yrong Nov 27, 2023
7f403d7
SetPricingParameters
yrong Nov 28, 2023
ffa59e4
Fix breaking tests
yrong Nov 28, 2023
6a4e9c0
Merge branch 'main' into remove-redundant-address
yrong Nov 28, 2023
c93468c
Merge main branch
yrong Nov 28, 2023
a6245fe
Merge branch 'main' into remove-redundant-address
yrong Nov 28, 2023
3a99601
Update sdk
yrong Nov 28, 2023
0b24172
Load InboundDeliveryCost from EthereumInboundQueue
yrong Nov 28, 2023
1618e7e
Fix e2e setup & smoke tests
yrong Nov 28, 2023
94e0949
Fix for compact scale
yrong Nov 28, 2023
2c0110f
Increase SECONDS_PER_SLOT to 2
yrong Nov 28, 2023
dba9769
Merge main branch
yrong Nov 28, 2023
a4a62a5
Update sdk
yrong Nov 28, 2023
59d3f20
merge main
yrong Nov 28, 2023
5b65043
Update sdk
yrong Nov 28, 2023
40d07ce
Simplify convert logic
yrong Nov 29, 2023
a396ad3
Format code
yrong Nov 29, 2023
9e32156
Add smoke test for set pricing params
yrong Nov 29, 2023
8d19ddb
Load fee params from env
yrong Nov 29, 2023
8333be7
Add test for convert currency
yrong Nov 29, 2023
e08c279
Load price configs from env
yrong Nov 29, 2023
7a68d89
Increase fee for test as enough
yrong Nov 29, 2023
edf418d
Remove createAssetExecutionFee
yrong Nov 29, 2023
b719ad1
Update sdk
yrong Nov 29, 2023
ab55e8f
Rename fee getter functions to include a `quote` prefix
vgeddes Nov 29, 2023
d26b6bd
improve setTokenTransferFees API, and add benchmarks
vgeddes Nov 29, 2023
359784e
Bump polkadot-sdk
vgeddes Nov 29, 2023
c0b32ee
Refactor convertToNative logic
yrong Nov 30, 2023
770c965
Update polkadot-sdk
vgeddes Nov 30, 2023
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: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,6 @@
[submodule "contracts/lib/canonical-weth"]
path = contracts/lib/canonical-weth
url = https://github.com/Snowfork/canonical-weth
[submodule "contracts/lib/prb-math"]
path = contracts/lib/prb-math
url = https://github.com/PaulRBerg/prb-math
1 change: 1 addition & 0 deletions contracts/lib/prb-math
Submodule prb-math added at 77fa88
1 change: 1 addition & 0 deletions contracts/remappings.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ ds-test/=lib/ds-test/src/
forge-std/=lib/forge-std/src/
openzeppelin-contracts/=lib/openzeppelin-contracts/
openzeppelin/=lib/openzeppelin-contracts/contracts/
prb/math/=lib/prb-math/
4 changes: 1 addition & 3 deletions contracts/src/AgentExecutor.sol
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@ contract AgentExecutor {
/// @dev Execute a message which originated from the Polkadot side of the bridge. In other terms,
/// the `data` parameter is constructed by the BridgeHub parachain.
///
/// NOTE: There are no authorization checks here. Since this contract is only used for its code.
function execute(address, bytes memory data) external {
function execute(bytes memory data) external {
(AgentExecuteCommand command, bytes memory params) = abi.decode(data, (AgentExecuteCommand, bytes));
if (command == AgentExecuteCommand.TransferToken) {
(address token, address recipient, uint128 amount) = abi.decode(params, (address, address, uint128));
Expand All @@ -29,7 +28,6 @@ contract AgentExecutor {
/// @dev Transfer ether to `recipient`. Unlike `_transferToken` This logic is not nested within `execute`,
/// as the gateway needs to control an agent's ether balance directly.
///
/// NOTE: There are no authorization checks here. Since this contract is only used for its code.
function transferNative(address payable recipient, uint256 amount) external {
recipient.safeNativeTransfer(amount);
}
Expand Down
81 changes: 59 additions & 22 deletions contracts/src/Assets.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {SafeTokenTransferFrom} from "./utils/SafeTransfer.sol";

import {AssetsStorage} from "./storage/AssetsStorage.sol";
import {SubstrateTypes} from "./SubstrateTypes.sol";
import {ParaID, MultiAddress} from "./Types.sol";
import {ParaID, MultiAddress, Ticket, Costs} from "./Types.sol";
import {Address} from "./utils/Address.sol";

/// @title Library for implementing Ethereum->Polkadot ERC20 transfers.
Expand All @@ -23,14 +23,6 @@ library Assets {
error InvalidDestination();
error Unsupported();

// This library requires state which must be initialized in the gateway's storage.
function initialize(uint256 registerTokenFee, uint256 sendTokenFee) external {
AssetsStorage.Layout storage $ = AssetsStorage.layout();

$.registerTokenFee = registerTokenFee;
$.sendTokenFee = sendTokenFee;
}

/// @dev transfer tokens from the sender to the specified
function _transferToAgent(address assetHubAgent, address token, address sender, uint128 amount) internal {
if (!token.isContract()) {
Expand All @@ -44,54 +36,99 @@ library Assets {
IERC20(token).safeTransferFrom(sender, assetHubAgent, amount);
}

function sendTokenCosts(ParaID assetHubParaID, ParaID destinationChain, uint128 destinationChainFee)
external
view
returns (Costs memory costs)
{
return _sendTokenCosts(assetHubParaID, destinationChain, destinationChainFee);
}

function _sendTokenCosts(ParaID assetHubParaID, ParaID destinationChain, uint128 destinationChainFee)
internal
view
returns (Costs memory costs)
{
AssetsStorage.Layout storage $ = AssetsStorage.layout();
if (assetHubParaID == destinationChain) {
costs.foreign = $.assetHubReserveTransferFee;
} else {
// If the final destination chain is not AssetHub, then the fee needs to additionally
// include the cost of executing an XCM on the final destination parachain.
costs.foreign = $.assetHubReserveTransferFee + destinationChainFee;
}
costs.native = 0;
}

function sendToken(
ParaID assetHubParaID,
address assetHubAgent,
address token,
address sender,
ParaID destinationChain,
MultiAddress calldata destinationAddress,
uint128 destinationChainFee,
uint128 amount
) external returns (bytes memory payload, uint256 extraFee) {
) external returns (Ticket memory ticket) {
AssetsStorage.Layout storage $ = AssetsStorage.layout();

_transferToAgent(assetHubAgent, token, sender, amount);
ticket.costs = _sendTokenCosts(assetHubParaID, destinationChain, destinationChainFee);

if (destinationChain == assetHubParaID) {
if (destinationAddress.isAddress32()) {
payload = SubstrateTypes.SendTokenToAssetHubAddress32(token, destinationAddress.asAddress32(), amount);
ticket.payload = SubstrateTypes.SendTokenToAssetHubAddress32(
token, destinationAddress.asAddress32(), $.assetHubReserveTransferFee, amount
);
} else {
// AssetHub does not support 20-byte account IDs
revert Unsupported();
}
} else {
if (destinationAddress.isAddress32()) {
payload = SubstrateTypes.SendTokenToAddress32(
token, destinationChain, destinationAddress.asAddress32(), amount
ticket.payload = SubstrateTypes.SendTokenToAddress32(
token,
destinationChain,
destinationAddress.asAddress32(),
$.assetHubReserveTransferFee,
destinationChainFee,
amount
);
} else if (destinationAddress.isAddress20()) {
payload = SubstrateTypes.SendTokenToAddress20(
token, destinationChain, destinationAddress.asAddress20(), amount
ticket.payload = SubstrateTypes.SendTokenToAddress20(
token,
destinationChain,
destinationAddress.asAddress20(),
$.assetHubReserveTransferFee,
destinationChainFee,
amount
);
} else {
revert Unsupported();
}
}
extraFee = $.sendTokenFee;

emit IGateway.TokenSent(sender, token, destinationChain, destinationAddress, amount);
}

/// @dev Enqueues a create native token message to substrate.
/// @param token The ERC20 token address.
function registerToken(address token) external returns (bytes memory payload, uint256 extraFee) {
function registerTokenCosts() external view returns (Costs memory costs) {
return _registerTokenCosts();
}

function _registerTokenCosts() internal view returns (Costs memory costs) {
AssetsStorage.Layout storage $ = AssetsStorage.layout();
costs.foreign = $.assetHubCreateAssetFee;
costs.native = $.registerTokenFee;
}

/// @dev Enqueues a create native token message to substrate.
/// @param token The ERC20 token address.
function registerToken(address token) external returns (Ticket memory ticket) {
if (!token.isContract()) {
revert InvalidToken();
}

payload = SubstrateTypes.RegisterToken(token);
extraFee = $.registerTokenFee;
ticket.costs = _registerTokenCosts();
ticket.payload = SubstrateTypes.RegisterToken(token);
vgeddes marked this conversation as resolved.
Show resolved Hide resolved

emit IGateway.TokenRegistrationSent(token);
}
Expand Down
16 changes: 12 additions & 4 deletions contracts/src/DeployScript.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,14 @@ import {AgentExecutor} from "./AgentExecutor.sol";
import {ChannelID, ParaID, OperatingMode} from "./Types.sol";
import {SafeNativeTransfer} from "./utils/SafeTransfer.sol";
import {stdJson} from "forge-std/StdJson.sol";
import {UD60x18, ud60x18, convert} from "prb/math/src/UD60x18.sol";

contract DeployScript is Script {
using SafeNativeTransfer for address payable;
using stdJson for string;

UD60x18 internal dotToEthDecimals;

function setUp() public {}

function run() public {
Expand Down Expand Up @@ -56,14 +59,17 @@ contract DeployScript is Script {
ParaID assetHubParaID = ParaID.wrap(uint32(vm.envUint("ASSET_HUB_PARAID")));
bytes32 assetHubAgentID = vm.envBytes32("ASSET_HUB_AGENT_ID");

dotToEthDecimals = convert(vm.envUint("DOT_TO_ETH_DECIMALS"));

AgentExecutor executor = new AgentExecutor();
Gateway gatewayLogic = new Gateway(
address(beefyClient),
address(executor),
bridgeHubParaID,
bridgeHubAgentID,
assetHubParaID,
assetHubAgentID
assetHubAgentID,
dotToEthDecimals
);

bool rejectOutboundMessages = vm.envBool("REJECT_OUTBOUND_MESSAGES");
Expand All @@ -76,9 +82,11 @@ contract DeployScript is Script {

Gateway.Config memory config = Gateway.Config({
mode: defaultOperatingMode,
outboundFee: vm.envUint("DEFAULT_FEE"),
registerTokenFee: vm.envUint("REGISTER_NATIVE_TOKEN_FEE"),
sendTokenFee: vm.envUint("SEND_NATIVE_TOKEN_FEE")
deliveryCost: uint128(vm.envUint("DELIVERY_COST")),
registerTokenFee: uint128(vm.envUint("REGISTER_TOKEN_FEE")),
assetHubCreateAssetFee: uint128(vm.envUint("CREATE_ASSET_FEE")),
assetHubReserveTransferFee: uint128(vm.envUint("RESERVE_TRANSFER_FEE")),
exchangeRate: ud60x18(vm.envUint("EXCHANGE_RATE"))
});

GatewayProxy gateway = new GatewayProxy(address(gatewayLogic), abi.encode(config));
Expand Down
Loading