solana_ruby_wallet_adapter 0.1.2 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 688ccabfbcf81a0b1760ffddb3ad8e60698a64a0521ce469ee89c180ef877cbe
4
- data.tar.gz: a774975e1fa73ee4cac0f5799f19cd70a112da3ec2ec55dbe24aae413735277c
3
+ metadata.gz: 51811ed20a2a99f1c970eb5fefbaef2d8186355df44758ea919964df3ac561af
4
+ data.tar.gz: 8c8b14961ddcf9838ebfdaea5a73f42cff8a683ee76bd4603a69eeaa630f4f13
5
5
  SHA512:
6
- metadata.gz: c3bf635e569af35e0b8fcdbbcae0d5cc7791f4d1eee2db9a9167215f14b0afd2a75c9fe409d5390c8847c536b6ea10c46bad3a3b5f85e1dcf2f3c5b978ca8126
7
- data.tar.gz: 643cca94b8474106490d02551d29004a6bf28f97f0874e6c1230deff25ef84168ac4c8d349d7f9e19c23d31a71c1f42e1c05a51a5e15aadf240efb36f78e9b15
6
+ metadata.gz: 3de9b50d9b861222d103dcaf7f06616a26f8447bed66bf4d433afbce15e2326732f52a29a91c4bbc740e832f56a9d0546c9f44983aa14ef3c3d6a3521e406741
7
+ data.tar.gz: 8d2d223f9be47268939f5c4b99d3d20cca149f22a9ebd771c036191167277e7da466837a6576dda773708a664ee781dd5840a0656363d9bc03707f635ef2df47
data/README.md CHANGED
@@ -361,24 +361,40 @@ picker and handles any server-side signature work.
361
361
  **Flow:**
362
362
  1. Rails layout seeds registered wallet metadata into `window.__SOLANA_WALLETS__`.
363
363
  2. A Stimulus controller detects the wallet, connects, and collects the SOL amount.
364
- 3. The browser POSTs `{ public_key, sol }` to a Rails endpoint.
365
- 4. The Rails controller builds a `createAccount + initialize + delegate` transaction,
364
+ 3. The browser POSTs `{ public_key, sol }` to `/staking/prepare`.
365
+ 4. The Rails controller builds a `createAccount + delegate` transaction,
366
366
  partially signs it with the generated stake-account keypair, and returns
367
367
  the wire bytes as Base64.
368
- 5. The Stimulus controller passes the bytes to the wallet for the user's signature,
369
- then broadcasts the fully-signed transaction.
368
+ 5. The Stimulus controller passes the bytes to the wallet for the user's signature.
369
+ 6. The signed wire bytes are POSTed to `/staking/submit`.
370
+ 7. Rails verifies every Ed25519 signature via `WalletStandard`, then broadcasts
371
+ to the cluster and returns the transaction signature.
370
372
 
371
- #### Layoutseed wallet metadata
373
+ #### EnvironmentRPC credentials
372
374
 
373
- ```erb
374
- <%# app/views/layouts/application.html.erb %>
375
- <head>
376
- <%# ... %>
377
- <script>window.__SOLANA_WALLETS__ = <%= solana_wallets_json.html_safe %>;</script>
378
- </head>
375
+ Store your RPC API key in `.env` (already gitignored by Rails):
376
+
377
+ ```
378
+ # .env
379
+ HELIUS_API_KEY=your-api-key-here
379
380
  ```
380
381
 
381
- #### Initializer — register adapters
382
+ #### Initializer — configure solana-ruby-kit and register adapters
383
+
384
+ ```ruby
385
+ # config/initializers/solana_ruby_kit.rb
386
+ Solana::Ruby::Kit.configure do |config|
387
+ if Rails.env.production?
388
+ config.rpc_url = "https://mainnet.helius-rpc.com/?api-key=#{ENV.fetch('HELIUS_API_KEY')}"
389
+ config.ws_url = "wss://mainnet.helius-rpc.com/?api-key=#{ENV.fetch('HELIUS_API_KEY')}"
390
+ else
391
+ config.rpc_url = "https://devnet.helius-rpc.com/?api-key=#{ENV.fetch('HELIUS_API_KEY')}"
392
+ config.ws_url = "wss://devnet.helius-rpc.com/?api-key=#{ENV.fetch('HELIUS_API_KEY')}"
393
+ end
394
+ config.commitment = :confirmed
395
+ config.timeout = 30
396
+ end
397
+ ```
382
398
 
383
399
  ```ruby
384
400
  # config/initializers/solana_wallet_adapter.rb
@@ -391,7 +407,21 @@ SolanaWalletAdapter::WalletRegistry.register(
391
407
  )
392
408
  ```
393
409
 
394
- #### Rails controller build and partially sign the transaction
410
+ #### Layoutseed wallet metadata
411
+
412
+ ```erb
413
+ <%# app/views/layouts/application.html.erb %>
414
+ <head>
415
+ <%# ... %>
416
+ <script>window.__SOLANA_WALLETS__ = <%= solana_wallets_json.html_safe %>;</script>
417
+ </head>
418
+ ```
419
+
420
+ #### Rails controller — build, verify, and broadcast
421
+
422
+ `Solana::Ruby::Kit.rpc_client` picks up the URL configured in the initializer.
423
+ `WalletStandard.verify_signed_transaction!` decodes the wire bytes returned by
424
+ the browser wallet and verifies every Ed25519 signature before broadcasting.
395
425
 
396
426
  ```ruby
397
427
  # app/controllers/staking_controller.rb
@@ -399,14 +429,15 @@ require 'base64'
399
429
 
400
430
  class StakingController < ApplicationController
401
431
  VOTE_ACCOUNT = '26RGqX3mezgYDxJnGh94gnMM4L2k9grH1eWcTSCHnaxR'
402
- RPC_URL = 'https://api.mainnet-beta.solana.com'
403
432
 
404
433
  # POST /staking/prepare
434
+ # Builds and partially signs a stake transaction server-side.
435
+ # Returns a base64-encoded wire transaction ready for the user's wallet to sign.
405
436
  def prepare
406
437
  public_key = params.require(:public_key)
407
438
  lamports = (params.require(:sol).to_f * 1_000_000_000).to_i
408
439
 
409
- rpc = Solana::Ruby::Kit::Rpc::Client.new(RPC_URL)
440
+ rpc = Solana::Ruby::Kit.rpc_client
410
441
  blockhash_result = rpc.get_latest_blockhash
411
442
  blockhash = blockhash_result.value.blockhash
412
443
  last_valid = blockhash_result.value.last_valid_block_height
@@ -414,7 +445,7 @@ class StakingController < ApplicationController
414
445
  owner = Solana::Ruby::Kit::Addresses::Address.new(public_key)
415
446
  stake_kp = Solana::Ruby::Kit::Keys.generate_key_pair
416
447
  stake_address = Solana::Ruby::Kit::Addresses::Address.new(
417
- Solana::Ruby::Kit::Encoding::Base58.encode(stake_kp.verify_key.to_bytes)
448
+ Solana::Ruby::Kit::Addresses.encode_address(stake_kp.verify_key.to_bytes)
418
449
  )
419
450
 
420
451
  create_ixs = Solana::Ruby::Kit::Programs::StakeProgram.create_account_instructions(
@@ -448,14 +479,34 @@ class StakingController < ApplicationController
448
479
  rescue => e
449
480
  render json: { error: e.message }, status: :unprocessable_entity
450
481
  end
482
+
483
+ # POST /staking/submit
484
+ # Receives a wallet-signed transaction (base64 wire bytes), verifies every
485
+ # Ed25519 signature server-side via WalletStandard, then broadcasts to the
486
+ # cluster and returns the transaction signature.
487
+ def submit
488
+ signed_b64 = params.require(:signed_transaction)
489
+ wire_bytes = Base64.strict_decode64(signed_b64)
490
+
491
+ tx = Solana::Ruby::Kit::WalletStandard.verify_signed_transaction!(wire_bytes)
492
+ Solana::Ruby::Kit::Transactions.assert_fully_signed_transaction!(tx)
493
+
494
+ rpc = Solana::Ruby::Kit.rpc_client
495
+ sig = rpc.send_transaction(signed_b64)
496
+
497
+ render json: { signature: sig.value }
498
+ rescue => e
499
+ render json: { error: e.message }, status: :unprocessable_entity
500
+ end
451
501
  end
452
502
  ```
453
503
 
454
- #### Route
504
+ #### Routes
455
505
 
456
506
  ```ruby
457
507
  # config/routes.rb
458
508
  post '/staking/prepare', to: 'staking#prepare'
509
+ post '/staking/submit', to: 'staking#submit'
459
510
  ```
460
511
 
461
512
  #### ViewComponent — Stimulus markup
@@ -494,20 +545,20 @@ post '/staking/prepare', to: 'staking#prepare'
494
545
 
495
546
  #### Stimulus controller — wallet connection and signing
496
547
 
548
+ The signed transaction is POSTed back to Rails for server-side verification and
549
+ broadcasting. No RPC credentials are needed in the browser.
550
+
497
551
  ```js
498
552
  // app/javascript/controllers/stake_controller.js
499
553
  import { Controller } from "@hotwired/stimulus"
500
- import { Connection, Transaction } from "@solana/web3.js"
501
-
502
- const RPC_URL = "https://api.mainnet-beta.solana.com"
554
+ import { Transaction } from "@solana/web3.js"
503
555
 
504
556
  export default class extends Controller {
505
557
  static targets = ["connectButton", "input", "stakeButton", "status"]
506
558
 
507
559
  connect() {
508
- this.provider = null
509
- this.publicKey = null
510
- this.connection = new Connection(RPC_URL, "confirmed")
560
+ this.provider = null
561
+ this.publicKey = null
511
562
  this.detectWallet()
512
563
  }
513
564
 
@@ -562,9 +613,16 @@ export default class extends Controller {
562
613
  const signedTx = await this.provider.signTransaction(tx)
563
614
 
564
615
  this.setStatus("Broadcasting…")
565
- const sig = await this.connection.sendRawTransaction(signedTx.serialize())
566
- await this.connection.confirmTransaction(sig, "confirmed")
567
- this.setStatus(`Staked! Tx: ${sig.slice(0, 20)}…`)
616
+ const signedB64 = btoa(String.fromCharCode(...signedTx.serialize()))
617
+ const submitResp = await fetch("/staking/submit", {
618
+ method: "POST",
619
+ headers: { "Content-Type": "application/json", "X-CSRF-Token": csrfToken },
620
+ body: JSON.stringify({ signed_transaction: signedB64 }),
621
+ })
622
+ const submitData = await submitResp.json()
623
+ if (submitData.error) throw new Error(submitData.error)
624
+
625
+ this.setStatus(`Staked! Tx: ${submitData.signature.slice(0, 20)}…`)
568
626
  } catch (e) {
569
627
  this.setStatus(`Error: ${e.message}`)
570
628
  this.stakeButtonTarget.disabled = false
@@ -577,10 +635,23 @@ export default class extends Controller {
577
635
  }
578
636
  ```
579
637
 
580
- > **Note:** `@solana/web3.js` is still needed in the browser to deserialize the
581
- > server-built transaction (`Transaction.from`) and broadcast it via
582
- > `sendRawTransaction`. All React and `@solana/wallet-adapter-react*` packages
583
- > can be removed from `package.json`.
638
+ #### esbuild IIFE format required
639
+
640
+ `@solana/web3.js` uses `import.meta` internally. Bundle as IIFE and define
641
+ `import.meta.url` away so the bundle runs as a plain script (no `type="module"`
642
+ needed):
643
+
644
+ ```json
645
+ // package.json
646
+ "build": "esbuild app/javascript/application.js --bundle --sourcemap --format=iife --define:global=globalThis --define:import.meta.url=undefined --outdir=app/assets/builds --public-path=/assets"
647
+ ```
648
+
649
+ > **Note:** `@solana/web3.js` is still needed in the browser solely to
650
+ > deserialize the server-built transaction (`Transaction.from`) before passing it
651
+ > to the wallet for signing. Broadcasting is handled server-side by
652
+ > `WalletStandard` + `rpc.send_transaction`, so `Connection` and `sendRawTransaction`
653
+ > are not used. All React and `@solana/wallet-adapter-react*` packages can be
654
+ > removed from `package.json`.
584
655
 
585
656
  ---
586
657
 
@@ -2,5 +2,5 @@
2
2
  # typed: strict
3
3
 
4
4
  module SolanaWalletAdapter
5
- VERSION = "0.1.2"
5
+ VERSION = "0.1.3"
6
6
  end
@@ -28,7 +28,7 @@ module SolanaWalletAdapter
28
28
  extend T::Sig
29
29
 
30
30
  # Register one or more adapter classes.
31
- sig { params(adapter_classes: T::Array[T.class_of(BaseWalletAdapter)]).void }
31
+ sig { params(adapter_classes: T.class_of(BaseWalletAdapter)).void }
32
32
  def register(*adapter_classes)
33
33
  adapter_classes.each do |klass|
34
34
  instance = klass.new
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: solana_ruby_wallet_adapter
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - pzupan