Module: Solace::Utils::PDA

Included in:
PublicKey
Defined in:
lib/solace/utils/pda.rb

Overview

Raised when Program Derived Address (PDA) generation fails.

This error is raised when attempting to derive a PDA that is invalid, typically because the seeds and program ID combination results in a point that lies on the Ed25519 curve (PDAs must be off-curve). This is a rare occurrence but can happen with certain seed combinations.

Examples:

Handling invalid PDA

begin
  pda = Solace::Utils::PDA.find_program_address(seeds, program_id)
rescue Solace::Utils::PDA::InvalidPDAError => e
  puts "Failed to derive PDA: #{e.message}"
end

See Also:

Since:

  • 0.0.1

Defined Under Namespace

Classes: InvalidPDAError

Constant Summary collapse

PDA_MARKER =

!@attribute PDA_MARKER PDA_MARKER is the marker used in PDA calculations

Since:

  • 0.0.1

'ProgramDerivedAddress'
MAX_BUMP_SEED =

!@attribute MAX_BUMP_SEED The maximum seed value for PDA calculations

Since:

  • 0.0.1

255

Class Method Summary collapse

Class Method Details

.create_program_address(seeds, program_id) ⇒ String

Creates a program address from seeds and program ID

Parameters:

  • seeds (Array)

    The seeds to use in the calculation

  • program_id (String)

    The program ID to use in the calculation

Returns:

  • (String)

    The program address

Raises:

Since:

  • 0.0.1



65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/solace/utils/pda.rb', line 65

def self.create_program_address(seeds, program_id)
  seed_bytes = seeds.map { |seed| seed_to_bytes(seed) }.flatten

  program_id_bytes = Solace::Utils::Codecs.base58_to_bytes(program_id)

  combined = seed_bytes + program_id_bytes + PDA_MARKER.bytes

  hash_bin = Digest::SHA256.digest(combined.pack('C*'))

  raise InvalidPDAError if Solace::Utils::Curve25519Dalek.on_curve?(hash_bin)

  Solace::Utils::Codecs.bytes_to_base58(hash_bin.bytes)
end

.find_program_address(seeds, program_id) ⇒ Array

Finds a valid program address by trying different seeds

Examples:

Find a PDA with bump seed

seeds = ['metadata', mint_address, 'edition']
program_id = 'metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s'

address, bump = Solace::Utils::PDA.find_program_address(seeds, program_id)

Parameters:

  • seeds (Array)

    The seeds to use in the calculation

  • program_id (String)

    The program ID to use in the calculation

Returns:

  • (Array)

    The program address and bump seed

Raises:

Since:

  • 0.0.1



48
49
50
51
52
53
54
55
56
57
# File 'lib/solace/utils/pda.rb', line 48

def self.find_program_address(seeds, program_id)
  MAX_BUMP_SEED.downto(0) do |bump|
    address = create_program_address(seeds + [bump], program_id)
    return [address, bump]
  rescue InvalidPDAError
    next
  end

  raise 'Unable to find a valid program address'
end

.looks_like_base58_address?(string) ⇒ Boolean

Checks if a string looks like a base58 address

Parameters:

  • string (String)

    The string to check

Returns:

  • (Boolean)

    True if the string looks like a base58 address, false otherwise

Since:

  • 0.0.1



100
101
102
103
# File 'lib/solace/utils/pda.rb', line 100

def self.looks_like_base58_address?(string)
  string.length.between?(32, 44) &&
    Solace::Utils::Codecs.valid_base58?(string)
end

.seed_to_bytes(seed) ⇒ Array

Prepares a list of seeds for creating a program address

Parameters:

  • seed (String, Integer, Array)

    The seed to prepare

Returns:

  • (Array)

    The prepared seeds

Since:

  • 0.0.1



83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/solace/utils/pda.rb', line 83

def self.seed_to_bytes(seed)
  case seed
  when String
    looks_like_base58_address?(seed) ? Solace::Utils::Codecs.base58_to_bytes(seed) : seed.bytes
  when Integer
    seed.between?(0, 255) ? [seed] : seed.digits(256)
  when Array
    seed
  else
    seed.to_s.bytes
  end
end