<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Adedeji Adebayo</title>
    <description>The latest articles on Forem by Adedeji Adebayo (@thewoodfish).</description>
    <link>https://forem.com/thewoodfish</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2682886%2Fb88b3e3d-32bb-4ffc-a2e0-cb7330b60371.png</url>
      <title>Forem: Adedeji Adebayo</title>
      <link>https://forem.com/thewoodfish</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/thewoodfish"/>
    <language>en</language>
    <item>
      <title>Automating the @solana/web3.js v1 @solana/kit Migration with a Codemod</title>
      <dc:creator>Adedeji Adebayo</dc:creator>
      <pubDate>Fri, 01 May 2026 15:57:40 +0000</pubDate>
      <link>https://forem.com/thewoodfish/automating-the-solanaweb3js-v1-solanakit-migration-with-a-codemod-3535</link>
      <guid>https://forem.com/thewoodfish/automating-the-solanaweb3js-v1-solanakit-migration-with-a-codemod-3535</guid>
      <description>&lt;p&gt;The Solana ecosystem moved from &lt;code&gt;@solana/web3.js&lt;/code&gt; v1 to the new &lt;code&gt;@solana/kit&lt;/code&gt; package, and the migration is non-trivial. Classes became factory functions, &lt;code&gt;Transaction&lt;/code&gt; building became a functional pipe, &lt;code&gt;PublicKey&lt;/code&gt; became a branded &lt;code&gt;Address&lt;/code&gt; string, and &lt;code&gt;Keypair&lt;/code&gt; became an async &lt;code&gt;KeyPairSigner&lt;/code&gt;. Oh, and every RPC call now returns a &lt;code&gt;RpcRequest&lt;/code&gt; that needs &lt;code&gt;.send()&lt;/code&gt; before it does anything.&lt;/p&gt;

&lt;p&gt;If you have hundreds of files using the old API, you don't want to do this by hand.&lt;/p&gt;

&lt;p&gt;This post walks through the codemod I built to automate the mechanical parts of the migration, what it handles, what it deliberately skips, and how to run it on your own codebase.&lt;/p&gt;




&lt;h2&gt;
  
  
  Automation Coverage
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;~85% of call-site changes handled deterministically&lt;/strong&gt; across 10 transforms. Here's the breakdown:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pattern&lt;/th&gt;
&lt;th&gt;Status&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;import ... from '@solana/web3.js'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅ Automated&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;clusterApiUrl('devnet')&lt;/code&gt; → literal URL&lt;/td&gt;
&lt;td&gt;✅ Automated&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;new Connection(url)&lt;/code&gt; → &lt;code&gt;createSolanaRpc(url)&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;✅ Automated&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;new PublicKey('string')&lt;/code&gt; → &lt;code&gt;address('string')&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;✅ Automated&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;Keypair.generate()&lt;/code&gt; → &lt;code&gt;await generateKeyPairSigner()&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;✅ Automated (async functions only)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;.publicKey&lt;/code&gt; → &lt;code&gt;.address&lt;/code&gt;, &lt;code&gt;.secretKey&lt;/code&gt; → &lt;code&gt;.privateKey&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;✅ Automated&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;SystemProgram.transfer({...})&lt;/code&gt; → &lt;code&gt;getTransferSolInstruction({...})&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;✅ Automated&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;sendAndConfirmTransaction(conn, tx, [signer])&lt;/code&gt; → kit signature&lt;/td&gt;
&lt;td&gt;✅ Automated&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;rpc.getBalance()&lt;/code&gt;, &lt;code&gt;rpc.getSlot()&lt;/code&gt;, etc. → add &lt;code&gt;.send()&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;✅ Automated&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;PublicKey&lt;/code&gt;, &lt;code&gt;Keypair&lt;/code&gt;, &lt;code&gt;TransactionInstruction&lt;/code&gt; type annotations&lt;/td&gt;
&lt;td&gt;✅ Automated&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Keypair.fromSecretKey(bytes)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;⚠️ Manual&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;new PublicKey(buffer)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;⚠️ Manual&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multi-instruction &lt;code&gt;Transaction.add().add()&lt;/code&gt; chains&lt;/td&gt;
&lt;td&gt;⚠️ Manual&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Commitment level configuration&lt;/td&gt;
&lt;td&gt;⚠️ Manual&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;sendAndConfirmTransactionFactory&lt;/code&gt; wiring&lt;/td&gt;
&lt;td&gt;⚠️ Manual&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The manual patterns are skipped intentionally — not because they're hard, but because getting them wrong silently (wrong variable name, missing &lt;code&gt;await&lt;/code&gt;, broken transaction structure) is worse than leaving them for a human to fix with full context.&lt;/p&gt;




&lt;h2&gt;
  
  
  What the Codemod Handles (10 Transforms)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Import Remapping
&lt;/h3&gt;

&lt;p&gt;The foundation — every other transform depends on imports being right first.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;- import { Connection, PublicKey, Keypair, SystemProgram, LAMPORTS_PER_SOL, sendAndConfirmTransaction } from '@solana/web3.js';
&lt;/span&gt;&lt;span class="gi"&gt;+ import { createSolanaRpc, createSolanaRpcSubscriptions, address, generateKeyPairSigner, LAMPORTS_PER_SOL, sendAndConfirmTransactionFactory } from '@solana/kit';
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each v1 named import is remapped to its kit equivalent. &lt;code&gt;SystemProgram&lt;/code&gt; is dropped here since &lt;code&gt;getTransferSolInstruction&lt;/code&gt; lives in &lt;code&gt;@solana-program/system&lt;/code&gt; — that import is injected by transform #6.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. clusterApiUrl() → literal URL
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;- const connection = new Connection(clusterApiUrl('devnet'));
&lt;/span&gt;&lt;span class="gi"&gt;+ const connection = new Connection('https://api.devnet.solana.com');
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Resolves the three known cluster names (&lt;code&gt;devnet&lt;/code&gt;, &lt;code&gt;mainnet-beta&lt;/code&gt;, &lt;code&gt;testnet&lt;/code&gt;) to their literal URL strings. This runs before the Connection transform so &lt;code&gt;02-connection&lt;/code&gt; sees a clean URL.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Connection → createSolanaRpc
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;- const connection = new Connection('https://api.devnet.solana.com', 'confirmed');
&lt;/span&gt;&lt;span class="gi"&gt;+ const connection = createSolanaRpc('https://api.devnet.solana.com');
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The commitment argument is deliberately dropped — kit handles commitment per-call, not per-connection. This is flagged in the manual step.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. PublicKey → address
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;- const toPubkey = new PublicKey('So11111111111111111111111111111111111111112');
- const systemKey = PublicKey.default;
&lt;/span&gt;&lt;span class="gi"&gt;+ const toPubkey = address('So11111111111111111111111111111111111111112');
+ const systemKey = address('11111111111111111111111111111111');
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Only string-literal arguments are transformed. &lt;code&gt;new PublicKey(someBuffer)&lt;/code&gt; is left untouched to prevent false positives — the buffer origin needs human context.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Keypair.generate() → generateKeyPairSigner()
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;  async function main() {
&lt;span class="gd"&gt;-   const payer = Keypair.generate();
&lt;/span&gt;&lt;span class="gi"&gt;+   const payer = await generateKeyPairSigner();
&lt;/span&gt;  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The transform checks that the enclosing function is &lt;code&gt;async&lt;/code&gt; before rewriting. If the function is synchronous, the call is left alone — introducing &lt;code&gt;await&lt;/code&gt; into a sync function would be a breaking change.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Keypair Property Access
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;- console.log(payer.publicKey);
- const secret = payer.secretKey;
&lt;/span&gt;&lt;span class="gi"&gt;+ console.log(payer.address);
+ const secret = payer.privateKey;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Applied only on variables discovered to be keypairs — via assignments (&lt;code&gt;Keypair.generate()&lt;/code&gt;, &lt;code&gt;generateKeyPairSigner()&lt;/code&gt;) and type annotations (&lt;code&gt;payer: Keypair&lt;/code&gt;, &lt;code&gt;signer: KeyPairSigner&lt;/code&gt;), plus a conservative baseline list of common names. No &lt;code&gt;.publicKey&lt;/code&gt; on unrelated objects gets touched.&lt;/p&gt;

&lt;h3&gt;
  
  
  7. SystemProgram.transfer → getTransferSolInstruction
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gi"&gt;+ import { getTransferSolInstruction } from '@solana-program/system';
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gd"&gt;- const ix = SystemProgram.transfer({
-   fromPubkey: payer.publicKey,
-   toPubkey: recipient,
-   lamports: amount * LAMPORTS_PER_SOL,
- });
&lt;/span&gt;&lt;span class="gi"&gt;+ const ix = getTransferSolInstruction({
+   source: payer.address,
+   destination: recipient,
+   amount: amount * LAMPORTS_PER_SOL,
+ });
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Argument keys are remapped (&lt;code&gt;fromPubkey→source&lt;/code&gt;, &lt;code&gt;toPubkey→destination&lt;/code&gt;, &lt;code&gt;lamports→amount&lt;/code&gt;) and the &lt;code&gt;@solana-program/system&lt;/code&gt; import is injected if not already present.&lt;/p&gt;

&lt;h3&gt;
  
  
  8. sendAndConfirmTransaction (Simple Case)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;- const sig = await sendAndConfirmTransaction(connection, tx, [payer]);
&lt;/span&gt;&lt;span class="gi"&gt;+ const sig = await sendAndConfirmTransaction(tx, { signers: [payer], commitment: 'confirmed' });
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The simple three-argument form is rewritten to the kit call signature. The output includes an inline comment reminding you to wire up &lt;code&gt;sendAndConfirmTransactionFactory({ rpc, rpcSubscriptions })&lt;/code&gt; — this requires knowing both variables in scope, which is context the transform can't safely infer.&lt;/p&gt;

&lt;h3&gt;
  
  
  9. RPC Method Calls → Add .send()
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;- const balance = await rpc.getBalance(pubkey);
- const slot = await rpc.getSlot();
&lt;/span&gt;&lt;span class="gi"&gt;+ const balance = await rpc.getBalance(pubkey).send();
+ const slot = await rpc.getSlot().send();
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In &lt;code&gt;@solana/kit&lt;/code&gt;, every RPC method returns a &lt;code&gt;RpcRequest&lt;/code&gt; — you have to call &lt;code&gt;.send()&lt;/code&gt; on it to actually execute the request. This transform covers all 40+ RPC methods and is idempotent: running it twice won't double the &lt;code&gt;.send()&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  10. Type Annotations
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;- function transfer(from: Keypair, to: PublicKey): Promise&amp;lt;TransactionInstruction&amp;gt; {
&lt;/span&gt;&lt;span class="gi"&gt;+ function transfer(from: KeyPairSigner, to: Address): Promise&amp;lt;IInstruction&amp;gt; {
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Targets &lt;code&gt;type_identifier&lt;/code&gt; AST nodes specifically — so &lt;code&gt;new PublicKey(...)&lt;/code&gt; (already handled by transform #4) and &lt;code&gt;import { Keypair }&lt;/code&gt; (handled by transform #1) are never touched twice. Also injects the matching &lt;code&gt;import type { Address, IInstruction, KeyPairSigner } from '@solana/kit'&lt;/code&gt; automatically.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Required Manual Work
&lt;/h2&gt;

&lt;p&gt;These patterns were intentionally skipped:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pattern&lt;/th&gt;
&lt;th&gt;Why&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Keypair.fromSecretKey(bytes)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Needs &lt;code&gt;createKeyPairSignerFromBytes()&lt;/code&gt; but the bytes source may need async handling&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;new PublicKey(buffer)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Buffer-to-address conversion is context-dependent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multi-instruction &lt;code&gt;Transaction.add().add()&lt;/code&gt; chains&lt;/td&gt;
&lt;td&gt;The functional &lt;code&gt;pipe()&lt;/code&gt; rewrite is too structurally different to produce safely&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Commitment levels&lt;/td&gt;
&lt;td&gt;Must be moved to individual RPC call options&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;sendAndConfirmTransactionFactory&lt;/code&gt; wiring&lt;/td&gt;
&lt;td&gt;Requires knowing both &lt;code&gt;rpc&lt;/code&gt; and &lt;code&gt;rpcSubscriptions&lt;/code&gt; in scope&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Each of these is either too risky to get wrong deterministically, or requires semantic understanding of the surrounding code. They are best handled with a follow-up AI pass using the patterns documented in the &lt;a href="https://github.com/anza-xyz/kit" rel="noopener noreferrer"&gt;@solana/kit migration guide&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Before and After (Full Example)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Before:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Connection&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;PublicKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Keypair&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;SystemProgram&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="nx"&gt;LAMPORTS_PER_SOL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sendAndConfirmTransaction&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@solana/web3.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;transferSol&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Keypair&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;toAddress&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;amountSol&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;PublicKey&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;connection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Connection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;clusterApiUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;devnet&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;confirmed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;toPubkey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PublicKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;toAddress&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ix&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;SystemProgram&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;transfer&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;fromPubkey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;payer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;publicKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;toPubkey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;lamports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;amountSol&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;LAMPORTS_PER_SOL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;balance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getBalance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;publicKey&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Balance:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;balance&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;toPubkey&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;After (automated):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;getTransferSolInstruction&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@solana-program/system&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createSolanaRpc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;generateKeyPairSigner&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="nx"&gt;LAMPORTS_PER_SOL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sendAndConfirmTransactionFactory&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@solana/kit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;KeyPairSigner&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@solana/kit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;transferSol&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;KeyPairSigner&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;toAddress&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;amountSol&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Address&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;connection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createSolanaRpc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://api.devnet.solana.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;toPubkey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;address&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;toAddress&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ix&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getTransferSolInstruction&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;payer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;destination&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;toPubkey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;amountSol&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;LAMPORTS_PER_SOL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;balance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getBalance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Balance:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;balance&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;toPubkey&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All nine changes in this example were made automatically.&lt;/p&gt;




&lt;h2&gt;
  
  
  How to Run It
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Via the shell script (most reliable)&lt;/span&gt;
git clone https://github.com/thewoodfish/codemon
bash codemon/solana-web3js-to-kit/migrate.sh ./path/to/your/project

&lt;span class="c"&gt;# Or run individual transforms via the codemod CLI&lt;/span&gt;
npx codemod jssg run &lt;span class="nt"&gt;--language&lt;/span&gt; tsx &lt;span class="se"&gt;\&lt;/span&gt;
  ./solana-web3js-to-kit/transforms/01-imports.ts &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--target&lt;/span&gt; ./your/project &lt;span class="nt"&gt;--no-interactive&lt;/span&gt; &lt;span class="nt"&gt;--allow-dirty&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Design Decisions
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Zero false positives over maximum coverage.&lt;/strong&gt; Every transform has a bail-early check: if the pattern isn't present in the file, it returns &lt;code&gt;null&lt;/code&gt; and the file is untouched. Non-string &lt;code&gt;PublicKey&lt;/code&gt; construction, sync functions calling &lt;code&gt;Keypair.generate()&lt;/code&gt;, and ambiguous &lt;code&gt;.publicKey&lt;/code&gt; accesses are all skipped rather than guessed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No jscodeshift.&lt;/strong&gt; The entire codemod uses &lt;code&gt;ast-grep&lt;/code&gt; (jssg), the Codemod platform's native engine. Patterns are expressed as AST templates rather than manual tree traversal, making them readable and auditable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Leave comments, not broken code.&lt;/strong&gt; Where a partial transform is applied, the output still compiles and an inline comment points exactly at what still needs doing. A broken file after a codemod run is worse than an unchanged one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Idempotency.&lt;/strong&gt; Every transform is safe to run multiple times. The RPC &lt;code&gt;.send()&lt;/code&gt; transform uses two-pass position tracking to guarantee no call ever gets doubled.&lt;/p&gt;




&lt;p&gt;Source: &lt;a href="https://github.com/thewoodfish/codemon" rel="noopener noreferrer"&gt;github.com/thewoodfish/codemon&lt;/a&gt; under &lt;code&gt;solana-web3js-to-kit/&lt;/code&gt;.&lt;/p&gt;

</description>
      <category>automation</category>
      <category>javascript</category>
      <category>tooling</category>
      <category>web3</category>
    </item>
  </channel>
</rss>
