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 +4 -4
- data/README.md +101 -30
- data/lib/solana_wallet_adapter/version.rb +1 -1
- data/lib/solana_wallet_adapter/wallet_registry.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 51811ed20a2a99f1c970eb5fefbaef2d8186355df44758ea919964df3ac561af
|
|
4
|
+
data.tar.gz: 8c8b14961ddcf9838ebfdaea5a73f42cff8a683ee76bd4603a69eeaa630f4f13
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|
|
365
|
-
4. The Rails controller builds a `createAccount +
|
|
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
|
-
|
|
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
|
-
####
|
|
373
|
+
#### Environment — RPC credentials
|
|
372
374
|
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
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
|
-
####
|
|
410
|
+
#### Layout — seed 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
|
|
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::
|
|
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
|
-
####
|
|
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 {
|
|
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
|
|
509
|
-
this.publicKey
|
|
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
|
|
566
|
-
await
|
|
567
|
-
|
|
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
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
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
|
|
|
@@ -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
|
|
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
|