Solana Python Client Generator, Powered by Codama
This package provides a Solana Python client renderer for Codama that generates Python client code from Solana program IDL files.
This tool is in beta, so please report any issues or feedback.
pnpm install codama-py
To use Codama-py independently (not as a library), you can clone the repository and use the built-in CLI tool:
# Clone the repository
git clone https://github.com/Solana-ZH/codama-py.git
cd codama-py
# Install dependencies
pnpm install
# Build the project
pnpm build
# Generate Python client from an IDL file
pnpm run genpy -i path/to/your/idl.json -d path/to/output/directory
Once you have a Codama IDL, you can use the renderVisitor
of this package to generate Python clients. You will need to provide the base directory where the generated files will be saved and an optional set of options to customize the output.
// node ./codama.mjs
import { renderVisitor } from 'codama-py';
const pathToGeneratedFolder = path.join(__dirname, 'clients', 'python', 'src', 'generated');
const options = {}; // See below.
codama.accept(renderVisitor(pathToGeneratedFolder, options));
.
├── accounts
│ ├── foo_account.py
│ └── __init__.py
├── instructions
│ ├── some_instruction.py
│ ├── other_instruction.py
│ └── __init__.py
├── types
│ ├── bar_struct.py
│ ├── baz_enum.py
│ └── __init__.py
├── errors
│ ├── custom.py
│ └── __init__.py
└── program_id.py
"borsh-construct>=0.1.0",
"anchorpy>=0.21.0",
"solana>=0.36.6",
"solders>=0.26.0",
from solders.hash import Hash
from solders.keypair import Keypair
from solders.message import Message
from solders.pubkey import Pubkey
from solders.transaction import Transaction
from solana.rpc.async_api import AsyncClient
from my_client.instructions import some_instruction
# call an instruction
foo_account = Keypair()
async with AsyncClient("http://127.0.0.1:8899") as client:
res = await client.is_connected()
# in real use, fetch this from an RPC
recent_blockhash = (await client.get_latest_blockhash()).value.blockhash
ix = some_instruction({
"foo_param": "...",
"bar_param": "...",
...
},
{
"foo_account": foo_account.pubkey(), # signer
"bar_account": Pubkey("..."),
...
})
msg = Message(instructions=[ix], payer=payer.pubkey())
try:
transaction = Transaction([foo_account], msg, recent_blockhash)
result = (await client.simulate_transaction(transaction))
print(result)
except BaseException as e:
print(f"BaseException failed: {e}")
return None
from solders.pubkey import Pubkey
from my_client.accounts import FooAccount
# fetch an account
addr = Pubkey("...")
acc = await FooAccount.fetch(connection, addr)
if acc is None:
# the fetch method returns null when the account is uninitialized
raise ValueError("account not found")
# convert to a JSON object
obj = acc.to_json()
print(obj)
# load from JSON
acc_from_json = FooAccount.from_json(obj)
# structs
from my_client.types import BarStruct
bar_struct = BarStruct(
some_field="...",
other_field="...",
)
print(bar_struct.to_json())
# enums
from my_client.types import bazEnum
tupleEnum = bazEnum.SomeTupleKind((True, False, "some value"))
structEnum = bazEnum.SomeStructKind({
"field1": "...",
"field2": "...",
})
discEnum = bazEnum.SomeDiscriminantKind()
print(tupleEnum.toJSON(), structEnum.toJSON(), discEnum.toJSON())
# types are used as arguments in instruction calls (where needed):
ix = some_instruction({
"some_struct_field": bar_struct,
"some_enum_field": tuple_enum,
# ...
}, {
# accounts
# ...
})
# in case of struct fields, it's also possible to pass them as objects:
ix = some_instruction({
"some_struct_field": {
"some_field": "...",
"other_field": "...",
},
# ...,
}, {
# accounts
# ...
})
from solana.rpc.core import RPCException
from my_client.errors import from_tx_error
from my_client.errors.custom import SomeCustomError
try:
await provider.send(tx, [payer])
except RPCException as exc:
parsed = from_tx_error(exc)
raise parsed from exc
The Program ID is generated based on the Program address provided in the IDL. If it is not present in the IDL, it needs to be manually filled in.
The generated code uses the AnchorPy code generation method and some underlying structures.
Support for Codama was added, and some data structures not supported by AnchorPy were included, such as FixedSizeType, SizePrefixType, HiddenSuffixType, HiddenPrefixType, and EnumIndexU32Type.