From f9ee1cf82014f6421c4269783f16d5cc16fd5deb Mon Sep 17 00:00:00 2001 From: LowK Date: Tue, 10 Oct 2023 10:41:24 +0700 Subject: [PATCH] Add Layer Zero Integrations Tips --- .gitignore | 5 +- ...10-LayerZero-Integrations-Security-Tips.md | 60 +++++++++++++++++++ package.json | 3 +- scripts/content-template.mdx | 10 ++++ scripts/new.js | 35 +++++++++++ 5 files changed, 111 insertions(+), 2 deletions(-) create mode 100644 content/projects/2023-10-10-LayerZero-Integrations-Security-Tips.md create mode 100644 scripts/content-template.mdx create mode 100644 scripts/new.js diff --git a/.gitignore b/.gitignore index 13f49c0..b60e28d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,7 @@ # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +.idea + Makefile yarn.lock # dependencies @@ -35,4 +38,4 @@ yarn-error.log* .vercel # Local Netlify folder -.netlify +.netlify \ No newline at end of file diff --git a/content/projects/2023-10-10-LayerZero-Integrations-Security-Tips.md b/content/projects/2023-10-10-LayerZero-Integrations-Security-Tips.md new file mode 100644 index 0000000..c68793c --- /dev/null +++ b/content/projects/2023-10-10-LayerZero-Integrations-Security-Tips.md @@ -0,0 +1,60 @@ +--- +title: LayerZero Integrations Security Tips +description: LayerZero integrations security tips for smart contract developers and auditors. +repository: +date: 2023-10-10 +published: true +featured: true +author: LowK +--- + +LayerZero is an omnichain interoperability protocol designed for lightweight message passing across chains. +LayerZero provides authentic and guaranteed message delivery with configurable trustlessness. +Needs more information about LayerZero check out the [docs](https://layerzero.gitbook.io/docs/). + +If you are a smart contract developer or auditor, you should be aware of the following security tips +when integrating LayerZero into your smart contracts. + +## Security Tips + +1. Calling `send()` with `{value: msg.value}` is because `send()` **requires a bit of native gas token** so the relayer can complete the message delivery on the destination chain. If you don't set this value [you might get this error](notion://www.notion.so/docs/evm-guides/error-messages/error-layerzero-relayer-fee-failed) when calling `endpoint.send()` + +2. In a received chain, A **msg.sender is the LZ endpoint**. Are there any mistakes? + +3. **Address Sanity Check** In a receiving chain + + Is SrcAddress whitelisted? + + Check the address size according to the source chain (e.g. address size == 20 bytes on EVM chains) to prevent a vector unauthenticated contract call. + +4. In a received chain, parse srcAddress to the address type that is just applied for EVM chains. **Is your application connected with non-e chain?** + +5. **Is there a set of trusted remote addresses**? The feature is available at some places that must be checked: + - retryPayload()::srcAddress + - hasStoredPayload()::srcAddress + - forceResumeReceive()::srcAddress + - setTrustedRemote()::path + - isTrustedRemote()::_srcAddress + - lzReceive()::_srcAddress +6. **Should implement an *Instant Finality Guarantee (IFG)*** + + Reverting in (user application) UA is cumbersome and expensive. It is more efficient to design your UA with IFG such that if a transaction was accepted at source, the transaction will be accepted at the remote. For example, Stargate has a credit management system (Delta Algorithm) to guarantee that if a swap was accepted at source, the destination must have enough asset to complete the swap, hence the IFG. + +7. **Tracking the nonce** + +8. **One action per message**: only one thing per message + +9. **Store failed message**: [example](https://solodit.xyz/issues/h-06-attacker-can-block-layerzero-channel-code4rena-velodrome-finance-velodrome-finance-git) + + If the message execution fails at the destination, try-catch it, and store it for future retries. From LayerZero's perspective, the message has been delivered. It is much cheaper and easier for your programs to recover from the last state at the destination chain. + Store a hash of the message payload is much cheaper than storing the whole message + + *sink*: not implement `blockingLzReceive()` + +10. **Gas for Message Types** + + *If your app includes multiple message types to be sent across chains,* compute a rough gas estimate at the destination chain per each message type. + + *Your message may fail for the out-of-gas exception at the destination if your app did not instruct the relayer to put extra gas on contract execution*. + + And the UA should enforce the gas estimate on-chain at the source chain to prevent users from inputting too low a value for gas. \ No newline at end of file diff --git a/package.json b/package.json index 0756b7c..58dd3d3 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,8 @@ "dev": "yarn refresh-content && next dev", "build": "yarn refresh-content && && next build", "start": "yarn refresh-content && next start", - "fmt": "pnpm rome check . --apply-suggested && pnpm rome format . --write" + "fmt": "pnpm rome check . --apply-suggested && pnpm rome format . --write", + "new": "node ./scripts/new.js" }, "dependencies": { "@next/font": "^13.4.3", diff --git a/scripts/content-template.mdx b/scripts/content-template.mdx new file mode 100644 index 0000000..fb47c7e --- /dev/null +++ b/scripts/content-template.mdx @@ -0,0 +1,10 @@ +--- +title: {{title}} +description: +repository: +date: {{date}} +published: false +featured: false +author: LowK +--- + diff --git a/scripts/new.js b/scripts/new.js new file mode 100644 index 0000000..de4ab68 --- /dev/null +++ b/scripts/new.js @@ -0,0 +1,35 @@ +// make a new post by copying the template +// and opening it in the editor +// Usage: node scripts/new.js <category> +function makePost(title, dir) { + if (!title) { + throw error('No title provided'); + } + if (!dir) { + dir = 'projects' + } + const fs = require('fs'); + const path = require('path'); + const template = path.join(__dirname, './content-template.mdx'); + const date = new Date().toISOString().split('T')[0]; + const filename = `${date}-${title.replace(/\s/g, '-')}.mdx`; + + let filepath = path.join(__dirname, `../content/${filename}`); + if (dir) { + filepath = path.join(__dirname, `../content/${dir}`); + if (!fs.existsSync(filepath)) { + fs.mkdirSync(filepath); + } + filepath = path.join(filepath, filename); + } + + const content = fs.readFileSync(template, 'utf-8') + .replace(/{{title}}/g, title) + .replace(/{{date}}/g, date); + + fs.writeFileSync(filepath, content); + console.log(`Created ${filepath}`); + return filepath; +} + +makePost(process.argv[2], process.argv[3]); \ No newline at end of file