Skip to content

Code Example for Solana Deposit

Below are example implementations for setting up and executing deposits into the Gas.zip Solana deposit program.

Deposits using the Solana method must include instruction data defining the output chains and destination address. Use the example code for an up-to-date spec of how to format the instruction data.

typescript
import { TransactionInstruction, PublicKey, Transaction, Connection } from '@solana/web3.js'
import { serialize, field, vec, fixedArray } from '@dao-xyz/borsh'
import bs58 from 'bs58'
 
// Constants
const PROGRAM_ID = new PublicKey('gasZT2bpe7mxu5wMgQbvry84vok5CuF3huCEokyC3qh')
const SYSTEM_PROGRAM_ID = new PublicKey('11111111111111111111111111111111')
const GAS_RECIPIENT = new PublicKey('gasZT2bpe7mxu5wMgQbvry84vok5CuF2huCEokyC3qh')
const PROGRAM_DISCRIMINATOR = [242, 35, 198, 137, 82, 225, 242, 182]
 
class DepositArgs {
  @field({ type: 'u64' })
  value: bigint
 
  @field({ type: vec('u16') })
  chains: number[]
 
  @field({ type: fixedArray('u8', 32) })
  to: number[]
 
  constructor(value: bigint, chains: number[], to: number[]) {
    this.value = value
    this.chains = chains
    this.to = to
  }
}
 
interface CreateDepositTransactionParams {
  connection: Connection
  payer: PublicKey
  value: bigint
  destinationChains: number[]
  destinationAddress: string
}
 
function addressToBytes(address: string): number[] {
  // Handle Solana addresses
  if (new RegExp(/[1-9A-HJ-NP-Za-km-z]{32,44}/).test(address)) {
    const rawAddr = Buffer.from(bs58.decode(address).subarray(1))
    return Array.from(rawAddr)
  }
 
  // Handle EVM addresses
  const arr: number[] = []
  let cleanAddress = address.slice(2).padStart(64, '0')
 
  while (cleanAddress !== '') {
    arr.push(Number(`0x${cleanAddress.slice(0, 2)}`) & 0xff)
    cleanAddress = cleanAddress.slice(2)
  }
 
  return arr
}
 
export async function createDepositTransaction({
  connection,
  payer,
  value,
  destinationChains,
  destinationAddress,
}: CreateDepositTransactionParams): Promise<Transaction> {
  const args = new DepositArgs(value, destinationChains, addressToBytes(destinationAddress))
 
  const serializedArgs = serialize(args)
 
  const instruction = new TransactionInstruction({
    keys: [
      { isSigner: true, isWritable: true, pubkey: payer },
      { isSigner: false, isWritable: false, pubkey: SYSTEM_PROGRAM_ID },
      { isSigner: false, isWritable: true, pubkey: GAS_RECIPIENT },
    ],
    programId: PROGRAM_ID,
    data: Buffer.from(PROGRAM_DISCRIMINATOR.concat(Array.from(serializedArgs))),
  })
 
  const transaction = new Transaction()
  const latestBlockhash = await connection.getLatestBlockhash()
 
  transaction.feePayer = payer
  transaction.recentBlockhash = latestBlockhash.blockhash
  transaction.add(instruction)
 
  return transaction
}
 
export interface SendDepositTransactionParams extends CreateDepositTransactionParams {
  signTransaction: (transaction: Transaction) => Promise<Transaction>
}
 
export async function sendDepositTransaction(params: SendDepositTransactionParams): Promise<string> {
  const { signTransaction, connection, ...createParams } = params
 
  const transaction = await createDepositTransaction({ connection, ...createParams })
  const signedTransaction = await signTransaction(transaction)
 
  const signature = await connection.sendRawTransaction(signedTransaction.serialize())
  await connection.confirmTransaction(signature)
 
  return signature
}