Skip to content

Commit c784854

Browse files
[INT-419] Solana OApp Aptos compatibility patch (LayerZero-Labs#1599)
1 parent 83d28a8 commit c784854

14 files changed

Lines changed: 890 additions & 54 deletions

File tree

examples/oapp-aptos-move/README.md

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -179,14 +179,17 @@ Ensure that in move.layerzero.config.ts, all of your evm contracts have the owne
179179
]
180180
```
181181

182-
If you are wiring solana to move-vm, create a file in `deployments/solana-mainnet/MyOFT.json` (or `deployments/solana-testnet/MyOFT.json` if you are using testnet) and add the following field:
182+
If you are wiring solana to move-vm, in your config add:
183183

184-
```json
185-
{
186-
"address": <oft-store-address-from-solana-deployment-folder>
187-
}
184+
```ts
185+
const solanaContract: OmniPointHardhat = {
186+
eid: EndpointId.SOLANA_V2_TESTNET as EndpointId,
187+
address: "YOUR_DEPLOYED_SOLANA_OAPP_ADDRESS",
188+
};
188189
```
189190

191+
Note: Solana OApp must be deployed with the Solana example.
192+
190193
Commands:
191194

192195
### To wire from EVM to Move-VM:

examples/oapp-aptos-move/scripts/aptos-move-send.ts

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
import {
1313
Account,
14+
AccountAddressInput,
1415
Aptos,
1516
AptosConfig,
1617
Ed25519PrivateKey,
@@ -21,25 +22,26 @@ import {
2122
SimpleTransaction,
2223
} from '@aptos-labs/ts-sdk'
2324
import * as dotenv from 'dotenv'
25+
import { ethers } from 'ethers'
2426

25-
import { EndpointId } from '@layerzerolabs/lz-definitions'
27+
import { Chain, EndpointId, getNetworkForChainId } from '@layerzerolabs/lz-definitions'
2628
import { Options } from '@layerzerolabs/lz-v2-utilities'
2729

2830
// Load environment variables from .env file
2931
dotenv.config()
3032

3133
// Validate required environment variables
32-
if (!process.env.APTOS_PRIVATE_KEY || !process.env.ACCOUNT_ADDRESS) {
33-
throw new Error('Please set APTOS_PRIVATE_KEY and ACCOUNT_ADDRESS in your .env file')
34+
if (!process.env.APTOS_PRIVATE_KEY || !process.env.APTOS_ACCOUNT_ADDRESS) {
35+
throw new Error('Please set APTOS_PRIVATE_KEY and APTOS_ACCOUNT_ADDRESS in your .env file')
3436
}
3537

3638
// Configuration constants
3739
const APTOS_PRIVATE_KEY = process.env.APTOS_PRIVATE_KEY
38-
const ACCOUNT_ADDRESS = process.env.ACCOUNT_ADDRESS
40+
const ACCOUNT_ADDRESS = process.env.APTOS_ACCOUNT_ADDRESS
3941

4042
// OApp configuration
41-
const OAPP_ADDRESS = '' // Set your OApp's address on Aptos
42-
const REMOTE_EID = EndpointId.BSC_V2_TESTNET // Destination chain endpoint ID
43+
const OAPP_ADDRESS = '0x' // Set your OApp's address on Aptos
44+
const REMOTE_EID = EndpointId.SOLANA_V2_TESTNET // Destination chain endpoint ID
4345
const NETWORK = Network.TESTNET // Aptos network configuration
4446

4547
// Initialize Aptos account and client
@@ -59,10 +61,23 @@ async function send() {
5961
const options = Options.newOptions().addExecutorLzReceiveOption(BigInt(30000))
6062
const extraOptions = options.toBytes()
6163

62-
// Prepare the message
63-
const message = 'Hello, EVM!'
64-
const messageBytes = new TextEncoder().encode(message)
65-
const messageArray = new Uint8Array(messageBytes)
64+
let messageArray: Uint8Array
65+
if (getNetworkForChainId(REMOTE_EID).chainName === Chain.SOLANA) {
66+
const message = 'Hello, Solana!'
67+
68+
const messageHex = ethers.utils.solidityPack(
69+
['uint256', 'bytes'],
70+
[ethers.utils.toUtf8Bytes(message).length, ethers.utils.toUtf8Bytes(message)]
71+
)
72+
// Convert hex string to Uint8Array
73+
messageArray = ethers.utils.arrayify(messageHex)
74+
} else {
75+
const message = 'Hello, EVM!'
76+
const messageBytes = new TextEncoder().encode(message)
77+
messageArray = new Uint8Array(messageBytes)
78+
}
79+
80+
// The Following is the code required for packaging a string message to be sent to Solana:
6681

6782
// Get fee quote for the cross-chain message
6883
const quote = await aptos.view({
@@ -79,7 +94,7 @@ async function send() {
7994
console.log('ZRO fee:', quote[1])
8095

8196
// Verify account has sufficient balance
82-
const balance = await aptos.account.getAccountAPTAmount({ accountAddress: ACCOUNT_ADDRESS })
97+
const balance = await aptos.account.getAccountAPTAmount({ accountAddress: ACCOUNT_ADDRESS as AccountAddressInput })
8398
console.log('Account balance:', Number(balance) / 100000000, 'APT')
8499

85100
if (Number(balance) < Number(quote[0])) {
@@ -96,7 +111,7 @@ async function send() {
96111

97112
// Create the transaction
98113
const transaction: SimpleTransaction = await aptos.transaction.build.simple({
99-
sender: ACCOUNT_ADDRESS,
114+
sender: ACCOUNT_ADDRESS as AccountAddressInput,
100115
data: payload,
101116
options: {
102117
maxGasAmount: 100000,
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
// NOTE: This config is an example for wiring Solana -> Aptos.
2+
// See wiring-to-aptos.md for more information.
3+
import { EndpointId } from '@layerzerolabs/lz-definitions'
4+
import { ExecutorOptionType } from '@layerzerolabs/lz-v2-utilities'
5+
import { OAppOmniGraphHardhat, OmniPointHardhat } from '@layerzerolabs/toolbox-hardhat'
6+
7+
enum MsgType {
8+
SEND = 1,
9+
SEND_AND_CALL = 2,
10+
}
11+
const aptosContract: OmniPointHardhat = {
12+
eid: EndpointId.APTOS_V2_TESTNET as EndpointId,
13+
address: 'YOUR_DEPLOYED_APTOS_CONTRACT_ADDRESS',
14+
}
15+
const solanaContract: OmniPointHardhat = {
16+
eid: EndpointId.SOLANA_V2_TESTNET as EndpointId,
17+
address: 'YOUR_DEPLOYED_SOLANA_OAPP_ADDRESS',
18+
}
19+
20+
const layerZeroDVNAptosAddress = '0x756f8ab056688d22687740f4a9aeec3b361170b28d08b719e28c4d38eed1043e'
21+
const layerZeroDVNSolanaAddress = '4VDjp6XQaxoZf5RGwiPU9NR1EXSZn2TP4ATMmiSzLfhb'
22+
23+
const config: OAppOmniGraphHardhat = {
24+
contracts: [
25+
{
26+
contract: solanaContract,
27+
config: {
28+
owner: 'YOUR_SOLANA_OWNER_ADDRESS',
29+
delegate: 'YOUR_SOLANA_OWNER_ADDRESS',
30+
},
31+
},
32+
{
33+
contract: aptosContract,
34+
config: {
35+
owner: 'YOUR_APTOS_ACCOUNT_ADDRESS',
36+
delegate: 'YOUR_APTOS_ACCOUNT_ADDRESS',
37+
},
38+
},
39+
],
40+
connections: [
41+
{
42+
from: aptosContract,
43+
to: solanaContract,
44+
config: {
45+
enforcedOptions: [
46+
{
47+
msgType: MsgType.SEND_AND_CALL,
48+
optionType: ExecutorOptionType.LZ_RECEIVE,
49+
gas: 5_000,
50+
value: 0,
51+
},
52+
],
53+
sendLibrary: '0xcc1c03aed42e2841211865758b5efe93c0dde2cb7a2a5dc6cf25a4e33ad23690',
54+
receiveLibraryConfig: {
55+
receiveLibrary: '0xcc1c03aed42e2841211865758b5efe93c0dde2cb7a2a5dc6cf25a4e33ad23690',
56+
gracePeriod: BigInt(0),
57+
},
58+
sendConfig: {
59+
executorConfig: {
60+
maxMessageSize: 10_000,
61+
executor: '0x93353700091200ef9fdc536ce6a86182cc7e62da25f94356be9421c6310b9585',
62+
},
63+
ulnConfig: {
64+
confirmations: BigInt(10),
65+
requiredDVNs: [layerZeroDVNAptosAddress],
66+
optionalDVNs: [],
67+
optionalDVNThreshold: 0,
68+
},
69+
},
70+
receiveConfig: {
71+
ulnConfig: {
72+
confirmations: BigInt(15),
73+
requiredDVNs: [layerZeroDVNAptosAddress],
74+
optionalDVNs: [],
75+
optionalDVNThreshold: 0,
76+
},
77+
},
78+
},
79+
},
80+
{
81+
from: solanaContract,
82+
to: aptosContract,
83+
config: {
84+
enforcedOptions: [
85+
{
86+
msgType: 1,
87+
optionType: ExecutorOptionType.LZ_RECEIVE,
88+
gas: 200_000,
89+
value: 0,
90+
},
91+
],
92+
sendLibrary: '7a4WjyR8VZ7yZz5XJAKm39BUGn5iT9CKcv2pmG9tdXVH',
93+
receiveLibraryConfig: {
94+
receiveLibrary: '7a4WjyR8VZ7yZz5XJAKm39BUGn5iT9CKcv2pmG9tdXVH',
95+
gracePeriod: BigInt(0),
96+
},
97+
sendConfig: {
98+
executorConfig: {
99+
maxMessageSize: 10_000,
100+
executor: 'AwrbHeCyniXaQhiJZkLhgWdUCteeWSGaSN1sTfLiY7xK',
101+
},
102+
ulnConfig: {
103+
confirmations: BigInt(15),
104+
requiredDVNs: [layerZeroDVNSolanaAddress],
105+
optionalDVNs: [],
106+
optionalDVNThreshold: 0,
107+
},
108+
},
109+
receiveConfig: {
110+
ulnConfig: {
111+
confirmations: BigInt(10),
112+
requiredDVNs: [layerZeroDVNSolanaAddress],
113+
optionalDVNs: [],
114+
optionalDVNThreshold: 0,
115+
},
116+
},
117+
},
118+
},
119+
],
120+
}
121+
export default config
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
### Wiring Solana to Move-VM (Aptos, Initia, Movement, etc.)
2+
3+
:warning: **Important Limitations for Solana ↔ Move-VM Pathways**
4+
5+
Currently, you can only perform **one-way wiring from Solana → Move-VM** using this example. For the reverse pathway (Move-VM → Solana), you must use the [OApp Aptos Move example](https://github.com/LayerZero-Labs/devtools/tree/main/examples/oapp-aptos-move). Please follow the deployment instructions in the README.md of the `oapp-aptos-move` example for deploying to Move-VM and configuring the Move-VM → Solana pathway.
6+
7+
#### Configuration Steps for Solana → Aptos
8+
9+
:information_source: **For bidirectional Solana ↔ Aptos communication, deploy both examples:**
10+
11+
- **Solana → Aptos**: Use this example
12+
- **Aptos → Solana**: Use the [OApp Aptos Move example](https://github.com/LayerZero-Labs/devtools/tree/main/examples/oapp-aptos-move)
13+
14+
#### Deployment Process
15+
16+
1. **Create and Deploy Solana Example**
17+
18+
- Create the Solana example: `LZ_ENABLE_SOLANA_OAPP_EXAMPLE=1 npx create-lz-oapp@latest`
19+
- Deploy to Solana following the deployment instructions in the Solana example README
20+
21+
2. **Create and Deploy Aptos Move Example**
22+
23+
- Create the Aptos Move example: `LZ_ENABLE_EXPERIMENTAL_MOVE_VM_EXAMPLES=1 npx create-lz-oapp@latest`
24+
- Deploy to Aptos following the deployment and initialization instructions in the Aptos example README
25+
26+
Your directory structure should look like this:
27+
28+
```
29+
your-folder/
30+
├── your-aptos-example/
31+
└── your-solana-example/
32+
```
33+
34+
3. **Configure Solana Example**
35+
36+
- Navigate to the Solana example directory
37+
- Use the example configuration at `./docs/move.layerzero.config.ts`
38+
- Fill out the configuration with your desired values
39+
- Replace `./layerzero.config.ts` with the completed `move.layerzero.config.ts` file
40+
41+
4. **Wire Solana to Aptos**
42+
43+
Execute the following commands in the Solana example directory:
44+
45+
```bash
46+
npx hardhat lz:oapp:solana:init-config --oapp-config move.layerzero.config.ts
47+
```
48+
49+
```bash
50+
npx hardhat lz:oapp:wire --oapp-config move.layerzero.config.ts
51+
```
52+
53+
If these commands succeed, you have successfully wired the Solana contract to the Aptos Move contract.
54+
55+
5. **Wire Aptos to Solana**
56+
57+
- Transfer the `move.layerzero.config.ts` file from the Solana example to the Aptos Move example directory
58+
- In the Aptos Move example directory, run:
59+
```bash
60+
pnpm run lz:sdk:move:wire --oapp-config move.layerzero.config.ts
61+
```
62+
63+
If this command succeeds, you are ready to test the bidirectional pathway.

0 commit comments

Comments
 (0)