diff --git a/content/module-3/1-create-your-messaging-protocol.md b/content/module-3/1-create-your-messaging-protocol.md index 51be99a..2e0165f 100644 --- a/content/module-3/1-create-your-messaging-protocol.md +++ b/content/module-3/1-create-your-messaging-protocol.md @@ -59,15 +59,7 @@ The `proof` in question is a merkle proof proving the inclusion of a receipt in **ERC20Messaging** emits, on a sending subnet, the following `TokenSent` event to detail which cross-subnet ERC20 transfer was requested: -```solidity -event TokenSent( - SubnetId indexed targetSubnetId, - string symbol, - address tokenAddress, - address receiver, - uint256 amount -); -``` + By passing the index of the corresponding log in the transaction receipt, the **ERC20Messaging** contract instance on the receiving subnet can retrieve the event log, parse it, and get all the data it needs to execute the ERC20 transfer, i.e., mint the right amount of the right token to the right recipient. @@ -109,41 +101,27 @@ The code of the first function is pretty much arbitrary and depends on what you 3- Emits the **CrossSubnetMessageSent** core event to announce the cross-subnet message -```solidity -function sendToken(SubnetId targetSubnetId, string calldata symbol, address receiver, uint256 amount) external { - if (_toposCoreAddr.code.length == uint256(0)) revert InvalidToposCore(); - Token memory token = getTokenBySymbol(symbol); - _burnTokenFrom(msg.sender, symbol, amount); // 1- Burn the requested amount of the requested token - emit TokenSent(targetSubnetId, symbol, token.addr, receiver, amount); // 2- Emit the TokenSent event - _emitMessageSentEvent(targetSubnetId); // 3- Emit the CrossSubnetMessageSent event -} -``` + Only the last line that emits the **CrossSubnetMessageEvent** is a requirement for all messaging protocols. So if we were to name the first function of **CustomMessaging** `doSomething`, `doSomething` would have the following structure: -```solidity + +``` function doSomething(SubnetId targetSubnetId, ...otherArgs) external { ... // 1- Do something ... // 2- Emit an event that describes the something _emitMessageSentEvent(targetSubnetId); // 3- Emit the CrossSubnetMessageSent event } ``` + ### Receiving subnet (2) Let's now take a look at the second function (`_execute`) signature: -```solidity -function _execute( - uint256[] memory logIndexes, - address[] memory logsAddress, - bytes[] memory logsData, - bytes32[][] memory logsTopics, - SubnetId networkSubnetId -) internal virtual {} -``` + As detailed before, this function's signature is defined in **ToposMessaging**'s interface and the function is itself called by **ToposMessaging**'s `execute` function, so as a messaging protocol developer, you need not care about how all these arguments are retrieved. You only need to care about how you use them. @@ -172,13 +150,15 @@ Should you have other logs needed to decode in order to execute the cross-subnet Now that you have retrieved the right log index, you can retrieve the rest of the log's data you are interested in: -```solidity + +``` # pseudo-code myLogIndex = logIndexes[0] // myLog == 4 logsAddress[myLogIndex] // This is the address of the contract which emitted that log logsData[myLogIndex] // This is the data field of that log logsTopics[myLogIndex] // This is the topics field of that log ``` + From the address of the contract which emitted the log, you can verify that the address is the one you expected. From the data field, you can retrieve all the data that semantically describe the cross-subnet message. Eventually, from the topics you can retrieve all the indexed arguments of the log. @@ -186,61 +166,46 @@ From the address of the contract which emitted the log, you can verify that the **ERC20Messaging**'s `_execute` function is coded as follows: -```solidity -function _execute( - uint256[] memory logIndexes, - address[] memory logsAddress, - bytes[] memory logsData, - bytes32[][] memory logsTopics, - SubnetId networkSubnetId -) internal override { - uint256 tokenSentEventIndex = logIndexes[0]; - if (logsAddress[tokenSentEventIndex] != address(this)) revert InvalidOriginAddress(); - - bytes32 targetSubnetId = logsTopics[tokenSentEventIndex][1]; - if (SubnetId.unwrap(networkSubnetId) != targetSubnetId) revert InvalidSubnetId(); - - (string memory symbol, , address receiver, uint256 amount) = abi.decode( - logsData[tokenSentEventIndex], - (string, address, address, uint256) - ); - _mintToken(symbol, receiver, amount); -} -``` + -
As we can see, it starts by retrieving the expected index of the `TokenSent` event. -```solidity + +``` uint256 tokenSentEventIndex = logIndexes[0]; ``` + + Again, we know that the event is at index `0` because it is the only event we need to decode the ERC20 transfer that we need to execute on the receiving subnet, and because **ERC20Messaging**'s smart contract and frontend application were developed to work with that event only. -
Then, from the right index, the address of the contract that emitted the `TokenSent` event is retrieved and compared to the current address (the address of the **ERC20Messaging** smart contract instance being used). This is because **ERC20Messaging** expects all instances of its contract to be deployed at the same address on all subnets. -```solidity + +``` if (logsAddress[tokenSentEventIndex] != address(this)) revert InvalidOriginAddress(); ``` + -
Next, from the right index and the corresponding array of topics, the target subnet id is retrieved. Index `1` is used because the first topic of an EVM event is the event signature, and the next are the indexed arguments of the event and `targetSubnetId` is the first and only indexed argument of the `TokenSent` event. -```solidity + +``` bytes32 targetSubnetId = logsTopics[tokenSentEventIndex][1]; if (SubnetId.unwrap(networkSubnetId) != targetSubnetId) revert InvalidSubnetId(); ``` + -
Finally, the data field is decoded and the data to be used to execute the cross-subnet ERC20 transfer is retrieved (token symbol, receiver address, and amount), before the transfer gets executed by minting the token. -```solidity + +``` (string memory symbol, , address receiver, uint256 amount) = abi.decode( logsData[tokenSentEventIndex], (string, address, address, uint256) ); _mintToken(symbol, receiver, amount); ``` + diff --git a/src/components/GitHubCodeBlock.tsx b/src/components/GitHubCodeBlock.tsx index 9a56cc8..76d6003 100644 --- a/src/components/GitHubCodeBlock.tsx +++ b/src/components/GitHubCodeBlock.tsx @@ -27,8 +27,8 @@ interface GitHubCodeBlockProps { highlights: string; // The lines from the fetched file to highlight. '3..5', '6, 9, 11', etc. nofetch: boolean; // If true, the code will not be fetched from GitHub. link: string; // The link to display. This overrides the default, which is to construct it from the github information. - nocopy: string; // An optional line or range of lines that will not be copied to the clipboard when the copy button is pressed. - copytrim: string; // An optional regular expression that will be used to trim the code before it + nocopy: boolean; // An optional line or range of lines that will not be copied to the clipboard when the copy button is pressed. + copytrim: string; // An optional regular expression that will be used to trim the code before it ia copied. separator: string; // A line or lines that will be followed by a visible separator line. nolinenumbers: boolean; // If true, line numbers will not be displayed. } @@ -80,10 +80,13 @@ const assembleContentUrl = ( firstLine = 0, lastLine = 9999 ) => { + const formattedPath = `${path}`.startsWith('/') ? path.substring(1) : path; + const formattedTag = tag ? `${tag}/` : ''; + if (firstLine > 0) { - return `https://github.com/${org}/${repo}/blob/${tag}/${path}#L${firstLine}-L${lastLine}`; + return `https://github.com/${org}/${repo}/blob/${formattedTag}${formattedPath}#L${firstLine}-L${lastLine}`; } else { - return `https://github.com/${org}/${repo}/blob/${tag}/${path}`; + return `https://github.com/${org}/${repo}/blob/${formattedTag}${formattedPath}`; } }; @@ -144,7 +147,7 @@ export const GitHubCodeBlock: React.FC< highlights = '', nofetch = false, link = null, - nocopy = '', + nocopy = false, copytrim = '', separator = '', nolinenumbers = false, @@ -225,12 +228,12 @@ export const GitHubCodeBlock: React.FC< finished = true; } else { finished = true; - const parsed_code = trimCode(atob(data.content), lines); + const raw_code = atob(data.content); + const parsed_code = trimCode(raw_code, lines); setCode(parsed_code); if (lines != '') { - console.log(lines); const [_firstLine, _lastLine] = firstAndLastLines( - parsed_code, + raw_code, lines ); setFirstLine(_firstLine + 1);