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

Contracts V2 #1300

Open
wants to merge 16 commits into
base: v2
Choose a base branch
from
Open

Contracts V2 #1300

wants to merge 16 commits into from

Conversation

vgeddes
Copy link
Collaborator

@vgeddes vgeddes commented Oct 7, 2024

Tasks:

  • V2 types
  • V2 inbound messaging
  • V2 outbound messaging
  • Refactor to support both V1 & V2
  • Re-implement native token registration for V2 & Kusama compatibility
  • Generate encoded XCM fragments for token registration
  • Split IGateway into IGatewayV1 and IGatewayV2
  • Remove duplicated error definitions scattered throughout code
  • Unit tests
  • Code documentation

contracts/src/types/V2.sol Outdated Show resolved Hide resolved
contracts/src/types/V2.sol Outdated Show resolved Hide resolved
@yrong yrong mentioned this pull request Oct 15, 2024
8 tasks
@yrong yrong mentioned this pull request Oct 18, 2024
8 tasks
@vgeddes vgeddes changed the title V2 Contracts V2 Oct 18, 2024
@vgeddes vgeddes changed the base branch from main to v2 October 19, 2024 09:56
@vgeddes vgeddes marked this pull request as ready for review October 19, 2024 09:56
Copy link

codecov bot commented Oct 19, 2024

Codecov Report

Attention: Patch coverage is 51.76152% with 178 lines in your changes missing coverage. Please review.

Please upload report for BASE (v2@6d11768). Learn more about missing BASE report.

Files with missing lines Patch % Lines
contracts/src/Gateway.sol 27.04% 88 Missing and 1 partial ⚠️
contracts/src/v2/Calls.sol 0.00% 50 Missing ⚠️
contracts/src/v2/Handlers.sol 0.00% 21 Missing ⚠️
contracts/src/Functions.sol 80.95% 8 Missing ⚠️
contracts/src/v1/Calls.sol 90.90% 5 Missing ⚠️
contracts/src/AgentExecutor.sol 0.00% 3 Missing ⚠️
contracts/src/upgrades/polkadot/Gateway202411.sol 33.33% 2 Missing ⚠️
Additional details and impacted files
@@          Coverage Diff          @@
##             v2    #1300   +/-   ##
=====================================
  Coverage      ?   60.22%           
=====================================
  Files         ?       23           
  Lines         ?      714           
  Branches      ?      120           
=====================================
  Hits          ?      430           
  Misses        ?      275           
  Partials      ?        9           
Flag Coverage Δ
solidity 60.22% <51.76%> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

_ensureOutboundMessagingEnabled();

// Wrap provided ether and transfer to AssetHub agent
address assetHubAgent = Functions.ensureAgent(Constants.ASSET_HUB_AGENT_ID);
Copy link
Contributor

@yrong yrong Oct 20, 2024

Choose a reason for hiding this comment

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

Since we know agent of asset-hub do exists in production. Is this check nessessary?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yeah the check is not necessary, I'll add a TODO.

contracts/src/v2/Calls.sol Outdated Show resolved Hide resolved
Comment on lines +97 to +98
bytes memory payload =
SubstrateTypes.encodePayloadV2(ticket.origin, ticket.assets, ticket.xcm);
Copy link
Contributor

Choose a reason for hiding this comment

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

Cool, A lot more simple now!

With ticket.xcm does that mean for V2 there is no need to construct xcm on chain any more?

UI will build the xcm and it's the responsibility of relayer to validate the xcm and check the relay is profitable or not.

On BH we just decode the xcm with

VersionedXcm::<()>::decode_with_depth_limit(
    MAX_XCM_DECODE_DEPTH,
    &mut data,
)

and forward it to AH transparently.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yeah, exactly.

Though for registration of native tokens, we need to control the XCM and so we need to build it on chain. See more details in 149c3e4.

Copy link
Contributor

Choose a reason for hiding this comment

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

Though for registration of native tokens, control the XCM and so we need to build it on chain

Still not sure fully understand why ticket.assets is required here, why not just build the xcm off chain?

@vgeddes I've removed the MessageConverter completely in yrong/polkadot-sdk#3 which I'd assume is not in use any more, correct me if I'm wrong.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Still not sure fully understand why ticket.assets is required here, why not just build the xcm off chain?

When the message gets to BH, we convert the payload.assets to instructions like ReserveAssetDeposited,etc, and then append the instructions in payload.xcm.

Or in other words, BH builds final XCM using:

finalXCM = concat(
assets_to_xcm(payload.asssets),
UniversalOrigin(Ethereum)
DescendOrigin(payload.origin)
payload.xcm
)

We can't trust the user to build the entire xcm offchain as they could mint any assets they want. Rather, they can only supply a fragment of the xcm.

Copy link
Contributor

@yrong yrong Oct 20, 2024

Choose a reason for hiding this comment

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

We can't trust the user to build the entire xcm offchain as they could mint any assets they want.

Yeah, that make sense.

Instead of convert the assets to xcm, can we just do some basic check on BH to verify the xcm is constructed correctly based upon the assets? e.g.

ensure!(xcm contains DescendOrigin(payload.origin))
ensure!(xcm contains ReserveAssetDeposited(asset)|WithdrawAsset(asset)) // Depends on the transfer type is ENA or PNA
...

So the point here is on chain we do validate rather than construct.

Copy link
Contributor

@yrong yrong Oct 21, 2024

Choose a reason for hiding this comment

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

Btw: for transfer I'm not sure DescendOrigin(userOrigin) make sense. For PNA we need to burn the asset locked in Ethereum sovereign on AH. IIUC that's why we change origin to UniversalOrigin(GlobalConsensus(network)) before WithdrawAsset here.

Copy link
Contributor

Choose a reason for hiding this comment

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

So the point here is on chain we do validate rather than construct.

The issue with this approach is that the validation needs to happen on the Ethereum side in order to do this right. The actual locking/unlocking/minting/burning of tokens occurs on the Ethereum side in solidity, which must match the Assets section of the XCM. If the validation happens on BH or in a relayer, and we reject the message, it is too late because we have already changed the state on the Ethereum side. This is why it is better to build the assets section of the XCM on BH to match what was done on the Ethereum side.

Related comment here: yrong/polkadot-sdk#3 (comment)

Copy link
Contributor

@yrong yrong Oct 25, 2024

Choose a reason for hiding this comment

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

it is too late because we have already changed the state on the Ethereum side

Just curious what will happen in this case. Is this something acceptable assuming we will implement error handing mechanism any way? The message with errored xcm will get trapped on BH and we allow end user to edit/repair it?

Something like we allow user to add fee for message pending from Substrate to Ethereum direction, assuming there is no relayer who will pick it up if it's not profitable.

Copy link
Contributor

Choose a reason for hiding this comment

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

Just curious what will happen in this case. Is this something acceptable assuming we will implement error handing mechanism any way?

I think if the message is queued and if the message is profitable, than once submitted to BH we should not fail for any reason (such as validating the xcm for example) and we should try to always push the assets to AH where they can get trapped if the user passes invalid xcm. And we should trust that we constrain the origin to stop users from doing things that they cannot within the xcm, instead of validating.

contracts/src/v2/Calls.sol Outdated Show resolved Hide resolved
/// @param leafProof A message proof used to verify that the message is in the merkle tree committed by the OutboundQueue pallet
/// @param headerProof A proof that the commitment is included in parachain header that was finalized by BEEFY.
/// @param rewardAddress Account on BH to credit delivery rewards
function v2_submit(
Copy link
Contributor

@yrong yrong Oct 22, 2024

Choose a reason for hiding this comment

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

should it also be in IGateway.sol? Meanwhile we may need another function isRelayed(nonce) to check if the nonce has been relayed.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Good catch!


struct Command {
uint8 kind;
uint256 gas;
Copy link
Contributor

Choose a reason for hiding this comment

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

Gas is uint64.

contracts/src/Constants.sol Show resolved Hide resolved

function testGetSet() public {
bitmap.set(384);
assertEq(bitmap.get(384), true);
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe test the bits around the set bit to make sure they remain unset.

Suggested change
assertEq(bitmap.get(384), true);
assertEq(bitmap.get(383), false);
assertEq(bitmap.get(384), true);
assertEq(bitmap.get(385), false);

Comment on lines +97 to +98
bytes memory payload =
SubstrateTypes.encodePayloadV2(ticket.origin, ticket.assets, ticket.xcm);
Copy link
Contributor

Choose a reason for hiding this comment

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

So the point here is on chain we do validate rather than construct.

The issue with this approach is that the validation needs to happen on the Ethereum side in order to do this right. The actual locking/unlocking/minting/burning of tokens occurs on the Ethereum side in solidity, which must match the Assets section of the XCM. If the validation happens on BH or in a relayer, and we reject the message, it is too late because we have already changed the state on the Ethereum side. This is why it is better to build the assets section of the XCM on BH to match what was done on the Ethereum side.

Related comment here: yrong/polkadot-sdk#3 (comment)


// Register Ethereum-native token on AHK, using `xcmFeeAHP` and `xcmFeeAHK`
// of `msg.value` to pay for execution on AHP and AHK respectively.
function registerTokenOnKusama(address token, uint128 xcmFeeAHP, uint128 xcmFeeAHK)
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe we should introduce a Network Enum here as a destination parameter, instead of having 2 registers methods. The network can simply contain the Polkadot networks.
https://github.com/paritytech/polkadot-sdk/blob/master/polkadot/xcm/src/v4/junction.rs/#L125

error TokenNotRegistered();
error InvalidAsset();

address public constant WETH_ADDRESS = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
Copy link
Contributor

Choose a reason for hiding this comment

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

Hardcoded WETH_ADDRESS?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

yeah, why would it need to be mutable?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Oh, I see, for Ethereum testnets, WETH will have a different address. got it. 👍

revert ChannelDoesNotExist();
}
// See docs for `IGateway.sendMessage`
function sendMessage(bytes calldata xcm, bytes[] calldata assets) external payable {
Copy link
Contributor

Choose a reason for hiding this comment

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

I think we should add a MultiAddress parameter that can be the asset claimer. If provided then we can use this account with the SetClaimer XCM instruction. If it is zero/empty address then we SetClaimer to the origin(msg.sender).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants