Skip to content

Commit

Permalink
Add Layer Zero Integrations Tips
Browse files Browse the repository at this point in the history
  • Loading branch information
lowk3v committed Oct 10, 2023
1 parent 9f00a54 commit f9ee1cf
Show file tree
Hide file tree
Showing 5 changed files with 111 additions and 2 deletions.
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

.idea

Makefile
yarn.lock
# dependencies
Expand Down Expand Up @@ -35,4 +38,4 @@ yarn-error.log*
.vercel

# Local Netlify folder
.netlify
.netlify
Original file line number Diff line number Diff line change
@@ -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.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
10 changes: 10 additions & 0 deletions scripts/content-template.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
title: {{title}}
description:
repository:
date: {{date}}
published: false
featured: false
author: LowK
---

35 changes: 35 additions & 0 deletions scripts/new.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// make a new post by copying the template
// and opening it in the editor
// Usage: node scripts/new.js <title> <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]);

0 comments on commit f9ee1cf

Please sign in to comment.