Skip to content

Added ts example #1

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
144 changes: 121 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,22 @@ A Cloudflare worker that extends Solana RPC with account parsing capabilities th

Start local development server:

wrangler dev
```bash
npx wrangler dev
```

You can start it with a custom rpc endpoint by setting the `RPC_ENDPOINT` environment variable.

```bash
npx wrangler dev --var RPC_ENDPOINT:"https://api.devnet.solana.com"
```

### Deploy

Deploy to Cloudflare to parse on the edge:

```bash
wrangler deploy
npx wrangler deploy
```

Set the `RPC_ENDPOINT` environment variable to the URL of the base rpc endpoint.
Expand All @@ -40,27 +47,36 @@ Example response:

```json
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"context": {
"slot": 322677507,
"apiVersion": "2.1.9"
},
"value": {
"lamports": 6876480,
"data": {
"text": "You are an AI agent ..."
},
"owner": "LLMrieZMpbJFwN52WgmBNMxYojrpRVYXdC1RCweEbab",
"executable": false,
"rentEpoch": 18446744073709552000,
"space": 860
}
}
"jsonrpc": "2.0",
"id": 1,
"result": {
"context": {
"slot": 322677507,
"apiVersion": "2.1.9"
},
"value": {
"lamports": 6876480,
"data": {
"text": "You are an AI agent ..."
},
"owner": "LLMrieZMpbJFwN52WgmBNMxYojrpRVYXdC1RCweEbab",
"executable": false,
"rentEpoch": 18446744073709552000,
"space": 860
}
}
}
```

or for running locally:

```bash
curl "http://localhost:8787" \
-X POST \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"getParsedAccountData","params":["FPxc7bcafdCQqHS8S1KX4ENCPP3vncxsKK3yRZ3mMzGn", {"encoding": "base64"}]}'
```

### 2. Fetch Multiple Parsed Accounts

```bash
Expand All @@ -79,19 +95,101 @@ curl -s "https://rpcx.magicblock.app" \
-X POST \
-H "Content-Type: application/json" \
-H "Rpc: https://api.mainnet-beta.solana.com/" \
-d '{"jsonrpc":"2.0","id":1,"method":"getParsedAccountData","params":["FPxc7bcafdCQqHS8S1KX4ENCPP3vncxsKK3yRZ3mMzGn"]}' | jq .
-d '{"jsonrpc":"2.0","id":1,"method":"getParsedAccountData","params":["5RgeA5P8bRaynJovch3zQURfJxXL3QK2JYg1YamSvyLb"]}' | jq .
```

### 4. Subscribe to Parsed Account Updates

Connect:

```bash
wscat -c "wss://rpcx.magicblock.app"
npx wscat -c "wss://rpcx.magicblock.app"
```

or for devnet:

```bash
npx wscat -c "wss://rpcx.magicblock.app" -H "Rpc: https://api.devnet.solana.com"
```

of for running locally:

```bash
npx wscat -c "ws://localhost:8787" -H "Rpc: https://api.devnet.solana.com"
```

Subscribe to updates:

```bash
{"jsonrpc":"2.0","id":1,"method":"subscribeParsedAccount","params":["5RgeA5P8bRaynJovch3zQURfJxXL3QK2JYg1YamSvyLb",{"encoding":"jsonParsed","commitment":"confirmed"}]}
{"jsonrpc":"2.0","id":1,"method":"subscribeParsedAccount","params":["F9xLoh5xxLFNb4wYnhAPm73VWyxgBTL1HiPFVEz6uW8X",{"encoding":"jsonParsed","commitment":"confirmed"}]}
```

### Ts example

You can also directly overwrite and use the getParsedAccountInfo method from the solana-web3.js library for example and call that from ts.
Run ts script to get parsed account data:

```bash
npx tsx getParsedAccountData.ts
```

Example response:

```bash
Fetching account: F9xLoh5xxLFNb4wYnhAPm73VWyxgBTL1HiPFVEz6uW8X
{
"context": {
"slot": 365515764
},
"value": {
"data": {
"parsed": {
"authority": "GsfNSuZFrT2r4xzSndnCSs9tTXwt47etPqU8yFVnDcXd",
"board": {
"data": [
[
0,
0,
0,
2
],
[
2,
0,
0,
0
],
[
16,
0,
0,
2
],
[
4,
16,
8,
4
]
]
},
"score": 92,
"gameOver": false,
"direction": 1,
"topTile": 0,
"newTileX": 0,
"newTileY": 3,
"newTileLevel": 2,
"xp": 0,
"level": 0
},
"program": "2o48ieM95rmHqMWC5B3tTX4DL7cLm4m1Kuwjay3keQSv",
"space": 800
},
"executable": false,
"lamports": 6458880,
"owner": "2o48ieM95rmHqMWC5B3tTX4DL7cLm4m1Kuwjay3keQSv",
"rentEpoch": 18446744073709552000
}
}
```
20 changes: 20 additions & 0 deletions getParsedAccountData.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { Connection, PublicKey } from '@solana/web3.js';

async function getParsedAccountData() {
console.log('Connecting to RPC...');
const connection = new Connection('http://localhost:8787', 'confirmed');

// Account we want to fetch
const accountPubkey = new PublicKey('F9xLoh5xxLFNb4wYnhAPm73VWyxgBTL1HiPFVEz6uW8X');
console.log('Fetching account:', accountPubkey.toString());

try {
// Fetch the raw account data
const accountInfo = await connection.getParsedAccountInfo(accountPubkey, 'confirmed');
console.log(JSON.stringify(accountInfo, null, 2));
} catch (error) {
console.error('Error fetching account data:', error);
}
}

getParsedAccountData().catch(console.error);
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"@types/bn.js": "^5.1.6",
"typescript": "^5.5.2",
"vitest": "~2.1.9",
"wrangler": "^3.109.2"
"wrangler": "^3.113.0"
},
"packageManager": "[email protected]+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e",
"dependencies": {
Expand Down
91 changes: 46 additions & 45 deletions src/handlers/getParsedAccountData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,58 +4,59 @@ import { Buffer } from 'buffer';
import { errorResponse, getIdl, decodeAccount } from '../utils/utils';

export async function handleGetParsedAccountData(
body: { id: string; params?: any },
provider: Provider,
rpcEndpoint: string,
env: Env,
ctx: ExecutionContext
body: { id: string; params?: any },
provider: Provider,
rpcEndpoint: string,
env: Env,
ctx: ExecutionContext
) {
const req = new Request(rpcEndpoint, {
method: 'POST',
const req = new Request(rpcEndpoint, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'POST, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type'
},
body: JSON.stringify({
jsonrpc: '2.0',
'Content-Type': 'application/json',
Accept: 'application/json',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'POST, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type',
},
body: JSON.stringify({
jsonrpc: '2.0',
id: body.id,
method: 'getAccountInfo',
params: [
body.params?.[0],
{ encoding: 'base64' }
]
})
});
const accountRes = await fetch(req);
method: 'getAccountInfo',
params: [body.params?.[0], { encoding: 'base64', commitment: 'confirmed' }],
}),
});
const accountRes = await fetch(req);
let accountInfo;
try{
accountInfo = await accountRes.json() as { result: { value: { data: any, owner: string } } };
}catch (error: unknown) {
try {
accountInfo = (await accountRes.json()) as { result: { value: { data: any; owner: string } } };
} catch (error: unknown) {
// @ts-ignore
return errorResponse(body.id, -32602, "Error parsing response", { error: error.message, account: body.params?.[0], statusCode: accountRes.status});
return errorResponse(body.id, -32602, 'Error parsing response', {
error: error.message,
account: body.params?.[0],
statusCode: accountRes.status,
});
}
if (accountInfo.result.value) {
const dataBuffer = Buffer.from(accountInfo.result.value.data[0], 'base64');
const owner = new PublicKey(accountInfo.result.value.owner);
const idl = await getIdl(owner, provider, env, ctx);
if (accountInfo.result.value) {
const dataBuffer = Buffer.from(accountInfo.result.value.data[0], 'base64');
const owner = new PublicKey(accountInfo.result.value.owner);
const idl = await getIdl(owner, provider, env, ctx);

if (!idl) {
return errorResponse(body.id, -32602, "IDL not found for program", { programId: owner.toString() });
}
if (!idl) {
return errorResponse(body.id, -32602, 'IDL not found for program', { programId: owner.toString() });
}

try {
const program = new Program(idl as Idl, provider);
accountInfo.result.value.data = decodeAccount(dataBuffer, program);
} catch (error: unknown) {
return errorResponse(body.id, -32602, "Failed to decode account data", {
error: error instanceof Error ? error.message : String(error),
account: body.params?.[0]
});
}
}
try {
const program = new Program(idl as Idl, provider);
accountInfo.result.value.data = decodeAccount(dataBuffer, program);
} catch (error: unknown) {
return errorResponse(body.id, -32602, 'Failed to decode account data', {
error: error instanceof Error ? error.message : String(error),
account: body.params?.[0],
});
}
}

return accountInfo;
return accountInfo;
}
Loading