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

Update Solana gateway docs #480

Merged
merged 2 commits into from
Oct 10, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
257 changes: 251 additions & 6 deletions src/pages/developers/chains/solana.mdx
Original file line number Diff line number Diff line change
@@ -1,9 +1,254 @@
On Solana the gateway is implemented as a Solana program.
To interact with universal applications from Solana, use the Solana Gateway. The
Solana Gateway allows you to deposit SOL (the native gas token of Solana) to an
externally-owned account (EOA) or a universal application on ZetaChain. You can
also deposit SOL and call a universal application in a single transaction using
the same `deposit` instruction.

Solana gateway supports:
**Note:** In the next version of the Solana Gateway, the `deposit` and
`deposit_and_call` functionalities will be separate functions.

- depositing SOL to a universal app or an account on ZetaChain
- depositing SOL and calling a universal app
- depositing SPL tokens (coming soon)
## Deposit SOL

https://github.com/zeta-chain/protocol-contracts-solana/
To deposit SOL to an EOA or a universal application on ZetaChain, use the
`deposit` instruction of the Solana Gateway program.

```rust
pub fn deposit(ctx: Context<Deposit>, amount: u64, memo: Vec<u8>) -> Result<()>
```

### Parameters

- `amount`: The amount of SOL (in lamports) to deposit.
- `memo`: A vector of bytes containing the receiver's address and an optional
payload.

### Memo Format

- **Minimum Length:** The `memo` must be at least 20 bytes long.
- **Maximum Length:** The `memo` must not exceed 512 bytes.
- **Structure:**
- **First 20 Bytes:** Receiver's address on ZetaChain (similar to an Ethereum
address).
- **Remaining Bytes (Optional):** Payload to be sent to the universal
application.

### Usage

When you call the `deposit` instruction, the specified amount of SOL is
transferred from your account to the Solana Gateway's Program Derived Account
(PDA). The `memo` parameter is used to specify the receiver's address and an
optional message. If the `memo` contains only the receiver's address (20 bytes),
the SOL will be deposited to the receiver without triggering a contract call. If
the `memo` contains additional data beyond the 20-byte address, it will initiate
a cross-chain call to the universal application with the provided payload.

**Example of Depositing SOL to an EOA:**

```rust
let receiver_address: [u8; 20] = [/* receiver's ZetaChain address */];
let amount_in_lamports: u64 = 1_000_000; // Amount in lamports (1 SOL = 1,000,000,000 lamports)

// Memo consists of only the receiver's address
let memo = receiver_address.to_vec();

gateway::deposit(
ctx,
amount_in_lamports,
memo,
)?;
```

**Example of Depositing SOL and Calling a Universal Application:**

```rust
let receiver_address: [u8; 20] = [/* universal application's ZetaChain address */];
let amount_in_lamports: u64 = 1_000_000; // Amount in lamports
let payload: Vec<u8> = b"Hello, ZetaChain!".to_vec();

// Memo consists of the receiver's address followed by the payload
let mut memo = receiver_address.to_vec();
memo.extend_from_slice(&payload);

gateway::deposit(
ctx,
amount_in_lamports,
memo,
)?;
```

### Notes

- The `receiver` is specified in the first 20 bytes of the `memo`.
- If the `memo` length is exactly 20 bytes (only the receiver's address),
depositing SOL will **not** trigger a contract call.
- If the `memo` length is greater than 20 bytes, depositing SOL will **trigger**
a cross-chain call to the universal application with the provided payload.
- After the deposit is processed, the receiver gets the ZRC-20 version of the
deposited SOL (e.g., ZRC-20 SOL).
- The `memo` must be at least 20 bytes (to include the receiver's address) and
at most 512 bytes.

## Calling `deposit` from the Frontend

To interact with the Solana Gateway from a frontend application, you can refer
to the [solanaDeposit function in the ZetaChain
Toolkit](https://github.com/zeta-chain/toolkit/blob/main/packages/client/src/solanaDeposit.ts).
This function demonstrates how to call the `deposit` instruction from the
frontend using Anchor and the Solana Web3.js library.

**High-Level Steps:**

1. **Load the User's Keypair:**

Load the user's Solana keypair to sign transactions.

```typescript
const keypair = await getKeypairFromFile(args.idPath);
const wallet = new anchor.Wallet(keypair);
```

2. **Set Up the Connection and Provider:**

Establish a connection to the Solana cluster and set up the Anchor provider.

```typescript
const connection = new anchor.web3.Connection(args.api);
const provider = new anchor.AnchorProvider(connection, wallet, anchor.AnchorProvider.defaultOptions());
anchor.setProvider(provider);
```

3. **Load the Gateway Program:**

Load the Gateway program using its IDL and program ID.

```typescript
const programId = new anchor.web3.PublicKey(Gateway_IDL.address);
const gatewayProgram = new anchor.Program(Gateway_IDL as anchor.Idl, programId, provider);
```

4. **Calculate the Deposit Amount:**

Convert the amount of SOL to lamports.

```typescript
const depositAmount = new anchor.BN(anchor.web3.LAMPORTS_PER_SOL * args.amount);
```

5. **Prepare the Memo:**

- **Recipient Address:** Convert the recipient's ZetaChain address to a
buffer.

```typescript
const recipientBuffer = Buffer.from(args.recipient.slice(2), "hex");
```

- **Message Formatting:** If there are additional parameters, encode them
using ABI encoding.

```typescript
const encodedParams = ethers.utils.defaultAbiCoder.encode(args.params[0], args.params[1]);
const paramsBuffer = Buffer.from(encodedParams.slice(2), "hex");
```

- **Combine Recipient and Parameters:**

```typescript
const memo = Buffer.concat([recipientBuffer, paramsBuffer]);
```

6. **Create and Send the Transaction:**

Create the `deposit` instruction and send the transaction.

```typescript
const depositInstruction = await gatewayProgram.methods
.deposit(depositAmount, memo)
.accounts({
pda: pdaAccount,
signer: wallet.publicKey,
systemProgram: anchor.web3.SystemProgram.programId,
})
.instruction();

const tx = new anchor.web3.Transaction().add(depositInstruction);

const txSignature = await anchor.web3.sendAndConfirmTransaction(connection, tx, [keypair]);

console.log("Transaction signature:", txSignature);
```

**Note:** The full code and implementation details can be found in the
[ZetaChain Toolkit
repository](https://github.com/zeta-chain/toolkit/blob/main/packages/client/src/solanaDeposit.ts).

### Recommendation on Message Formatting

We recommend formatting the message payload using ABI encoding so that it can be
easily decoded in a universal application on ZetaChain. ABI encoding ensures
compatibility and standardization when transmitting data across different
platforms.

**Example of ABI Encoding:**

```typescript
const ethers = require("ethers");

// Define the parameter types and values
const paramTypes = ["uint256", "string"];
const paramValues = [12345, "Hello, ZetaChain!"];

// Encode the parameters
const encodedParams = ethers.utils.defaultAbiCoder.encode(paramTypes, paramValues);

// Convert to Buffer or Uint8Array as needed
const paramsBuffer = Buffer.from(encodedParams.slice(2), "hex"); // Remove '0x' prefix
```

## Additional Information

- **Lamports and SOL:** 1 SOL equals 1,000,000,000 lamports. Ensure you convert
SOL amounts to lamports when specifying the `amount` parameter.
- **Address Format:** The receiver's address is specified in the first 20 bytes
of the `memo`, consistent with Ethereum-style addresses used on ZetaChain.
- **Program Derived Account (PDA):** The PDA is a special account used by the
Solana Gateway program to manage deposited funds securely.

## Future Support

- **Separate `deposit` and `deposit_and_call` Functions:** In the next version
of the Solana Gateway, the `deposit` and `deposit_and_call` functionalities
will be separate functions to improve clarity and usability.
- **SPL Tokens:** Support for SPL tokens (Solana's native token standard) is
coming soon. This will allow you to deposit SPL tokens and interact with
universal applications on ZetaChain using these tokens.
- **Calling Solana Programs from Universal Applications:** In the future,
universal applications on ZetaChain will be able to call Solana programs
directly, enabling more complex cross-chain interactions.

## Error Handling

The Solana Gateway program includes several error codes to handle different
failure scenarios:

- `SignerIsNotAuthority`: The signer is not authorized to perform the action.
- `DepositPaused`: Deposits are currently paused.
- `MemoLengthTooShort`: The `memo` payload is less than the minimum required
length of 20 bytes.
- `MemoLengthExceeded`: The `memo` payload exceeds the maximum allowed length of
512 bytes.

Ensure to handle these errors appropriately in your application.

## Revert Transactions

The Solana Gateway supports transaction reversion in case of failures. If a
cross-chain call fails on the ZetaChain side, the deposited SOL will be reverted
back to the original sender on Solana.

## Conclusion

The Solana Gateway provides a seamless way to interact with universal
applications on ZetaChain from Solana. By using the `deposit` instruction, you
can deposit SOL and optionally trigger cross-chain contract calls to universal
applications on ZetaChain by including a payload in the `memo`.
Loading