Deploy a Solana smart contract
Introduction
The Custody Infrastructure system provides a secure way to deploy Solana smart contract programs through the API client. The deployment process uses a seed-based approach to derive deterministic program addresses, allowing you to know your contract's address before deployment, which is essential for compiling programs that need to reference their own address.
This is an API process. Be sure you are comfortable making API calls and interpreting the responses. See Getting Started with the API for details.

Summary
The Custody Infrastructure API can be used to deploy and manage a Solana smart contract. The process first requires choosing a seed name and using it to derive the contract address. After compiling the smart contract, it is deployed to the blockchain after passing through the Custody Infrastructure approval process. Post deployment management of authority settings, reclaiming SOL and specifiying rent and data parameters is also available. All of these stages are available directly from the Custody Infrastructure API.
- How it works
- Prerequisites
- Choose a Seed
- Derive the Program Address
- Compile your smart contract
- Deploy the Program
- Understanding deployment progress
- Advanced options
- Manage the Authority settings
- Manage the rent amounts settings
- Manage the maximum data length
- Troubleshooting
- Endpoint deep dive *
*Endpoint deep dive - Expanded
setSolanaProgramAuthority
deploySolanaProgram
getSolanaMinimumBalanceForRentExemption
getSolanaProgramAddressFromSeed
closeSolanaBpfLoaderAccounts
How it works
The deployment process begins by choosing a unique seed, a short string that deterministically defines your program’s address when combined with your main Solana account.
Once the seed is selected, it’s used to derive the program address, which is essential for compiling programs that need to reference their own address.
Next, compile the program using the derived address as the program ID. This step binds your code to the specific address created from your seed.
Finally, deploy the compiled program using the same seed. The deployment process first confirms that the transaction passes the policy approval process and then sends the smart contract program to the Solana blockchain in multiple small transactions, each confirmed in sequence, ensuring that the entire program is uploaded securely.
%%{
init: {
'theme': 'base',
'themeVariables': {
'primaryColor': '#9B69FF',
'primaryTextColor': '#FFFFFF',
'primaryBorderColor': '#9B69FF',
'secondaryColor': '#CCCCCC',
'secondaryTextColor': '#000000',
'secondaryBorderColor': '#9B69FF',
'lineColor': '#9B69FF',
'arrowColor': '#9B69FF',
'fontFamily': 'Arial, sans-serif, bold',
'fontSize': '16px',
'textColor': '#000000'
}
}
}%%
sequenceDiagram
API User ->> API: Choose a seed and derive contract address
API ->> API User: Address returned
API User ->> API: Compile and deploy smart contract
API ->> Approval: Check required approvals
Approval -->> Blockchain: Deploy contract to Blockchain
Blockchain -->> API: Single transaction data returned to API
API -->> API User: Transaction appears in Transaction page with updated progress until complete
API User ->> API: Manage Authority state setSolanaProgramAuthority
API ->> Approval: Check required approvals
Approval -->> Blockchain: Update authority state contract to Blockchain
Approval -->> API: Return Transaction data
API -->> API User: User should use API calls to check updated smart contract data on the Blockchain
API User ->> API: Reclaim SOL rent closeSolanaBpfLoaderAccounts
API ->> Approval: Check required approvals
Approval -->> Blockchain: Update reclaimed SOL to Blockchain
Approval -->> API: Return Transaction data
API -->> API User: User should use API calls to check updated smart contract data on the Blockchain
API User ->> API: Specify a custom maximum data length deploySolanaProgram
API ->> Approval: Check required approvals
Approval -->> Blockchain: Update maximum data length to Blockchain
Approval -->> API: Return Transaction data
API -->> API User: User should use API calls to check updated smart contract data on the Blockchain
Why use seeds instead of ephemeral keypairs?
Traditional Solana deployment tools (like solana program deploy or anchor deploy) generate ephemeral keypairs for program accounts. However, this approach is not compatible with MPC systems like Custody Infrastructure, where key generation requires coordination between multiple parties.
The seed-based approach using the solana system program function createAccountWithSeed provides:
- Deterministic addresses that can be derived without generating new keys
- MPC compatibility by using existing account keys as the base
- Predictable program IDs before deployment for compilation
Best practices for choosing seeds
When selecting a seed for your program deployment:
- Use descriptive, unique seeds - Include the program name or purpose (e.g., "token-vault", "swap-pool")
- Include version numbers - For upgradeable programs, use versioning like "my-program-v1", "my-program-v2"
- Keep seeds concise - Maximum 32 bytes, but shorter is more efficient
- Avoid seed reuse - Do not use the same seed for different programs or contexts
- Combine static and dynamic elements - If deploying multiple similar programs, use a common prefix with unique suffixes
The smart contract deployment procedure
Prerequisites
Before deploying a Solana smart contract program, ensure that:
- A Deploy any Smart Contract transaction policy is enabled for your account. If you don't have one, make a new smart contract transaction policy and select Deploy any smart contract.

- You have sufficient SOL in your account to cover deployment costs (rent + transaction fees).
- Choose a seed
Select an arbitrary string to use as your program seed. This seed:
- can be any string up to 32 bytes in length
- is combined with your main account's Solana address to derive the program address
- must be used consistently throughout the deployment process
Example: "my-token-program-v1"
- Derive the program address
Use the API function getSolanaProgramAddressFromSeed() to get the program address that will be created:
program_address = api_client.getSolanaProgramAddressFromSeed(
account_id=your_account_id,
program_seed="my-token-program-v1"
)This address is deterministically derived by combining:
- the main account's Solana address (as the base)
- the seed you provided
- the BPF Loader Upgradeable program ID
Important: Save this address as you will need it in the next step.
- Compile your smart contract program
Compile your Solana smart contract using the derived contract address.
For Anchor programs, use the address in your declare_id!() macro:
use anchor_lang::prelude::*;
declare_id!("YourDerivedProgramAddressHere");
#[program]
pub mod my_program {
// Your program code
}For non-Anchor programs, configure your smart contract ID according to your framework's requirements.
- Deploy the smart contract program
Deploy your compiled smart contract program using the seed from Step 1:
# Read the compiled program binary
with open("path/to/your/program.so", "rb") as f:
program_bytes = f.read()
# Deploy with the same seed from Step 1
result = api_client.deploySolanaProgram(
account_id=your_account_id,
program_bytes=program_bytes,
program_seed="my-token-program-v1", # Same seed as Step 1
description="Deploy my token program v1"
)The deployment will be submitted as a transaction for approval according to your configured policies.
Understanding deployment progress
After deployment approval, the smart contract program appears as a single transaction in the Transactions page of the desktop application.
The transaction displays progress as "m/n transactions sent" (for example, "312/587 transactions sent"). This counter updates as the deployment progresses because programs are split into multiple smaller transactions that upload the program data in chunks. Average-sized programs typically require 500+ transactions to fully deploy.
Note: Program deployments may take several minutes to complete. The progress counter continues to update until all transactions have been sent and confirmed.
Advanced Options
Manage the Authority settings
By default, the account that deploys the program becomes its upgrade authority. To specify a different authority:
result = api_client.deploySolanaProgram(
account_id=your_account_id,
program_bytes=compiled_program_bytes,
program_seed="my-token-program-v1",
authority_address="CustomAuthorityAddressHere"
)After deployment, you can transfer or revoke the upgrade authority. Revoking the authority makes the program immutable and should only be performed once the program has been thoroughly validated and tested.
# Transfer authority to a new address
api_client.setSolanaProgramAuthority(
account_id=your_account_id,
program_address="YourProgramAddress",
new_authority_address="NewAuthorityAddress"
)
# Revoke authority (make program immutable)
api_client.setSolanaProgramAuthority(
account_id=your_account_id,
program_address="YourProgramAddress",
new_authority_address=None # Makes the program immutable
)Reclaiming SOL from deployment accounts
After deployment, buffer accounts hold rent-exempt SOL that can be reclaimed. You can also close program data accounts to reclaim their rent:
# Close buffer accounts to reclaim SOL
api_client.closeSolanaBpfLoaderAccounts(
account_id=your_account_id,
destination_address="AddressToReceiveSOL",
buffer_accounts=["BufferAddress1", "BufferAddress2"]
)
# Close program data account (requires upgrade authority)
api_client.closeSolanaBpfLoaderAccounts(
account_id=your_account_id,
destination_address="AddressToReceiveSOL",
program_address="YourProgramAddress",
program_data_account="ProgramDataAccountAddress",
authority_address="UpgradeAuthorityAddress"
)Manage the rent amounts settings
The system automatically calculates required rent for deployment accounts. To specify custom rent amounts:
result = api_client.deploySolanaProgram(
account_id=your_account_id,
program_bytes=compiled_program_bytes,
program_seed="my-token-program-v1",
buffer_rent_lamports=custom_buffer_rent,
program_rent_lamports=custom_program_rent
)Manage the maximum data length
By default, the maximum data length is set to 2x the program size. To specify a custom maximum:
result = api_client.deploySolanaProgram(
account_id=your_account_id,
program_bytes=compiled_program_bytes,
program_seed="my-token-program-v1",
max_data_len=custom_max_data_length
)Troubleshooting
Issue | Symptoms/Error Messages | Solution |
|---|---|---|
Transaction policy not enabled | "Transaction policy not found" or similar authorization error | Ensure the "Deploy any Smart Contract" transaction policy is enabled for your account. |
Seed already used | Transaction simulation failed: Error processing Instruction 0: custom program error: 0x0 This error occurs when the solana system program function createAccountWithSeed tries to create an account that already exists. | The seed has been used before for a previous deployment. Choose a different seed or increment the version number (e.g., change "my-program-v1" to "my-program-v2"). |
Address mismatch | Address mismatch errors during deployment | Verify you are using the exact same seed in both getSolanaProgramAddressFromSeed() and deploySolanaProgram(). Seeds are case-sensitive. |
Deployment appears stuck | Progress display shows "m/n transactions sent" but stops updating | Program deployments can take several minutes to complete (500+ transactions is typical). If progress stops for more than 10 minutes, check network status and transaction logs. |
Insufficient funds  | Insufficient SOL errors or transaction failures | Ensure your account has enough SOL to cover both rent (for program and buffer accounts) and transaction fees for all deployment transactions. |
InsufficientFundsForRent error | Rent specified in buffer_rent_lamports or program_rent_lamports is too low. Note: If these are not defined the system autmatically gets the values from the blockchain. |

Endpoint Deep Dive
var myHeaders = new Headers();
myHeaders.append("Accept", "application/json");
myHeaders.append("Content-Type", "application/json");
var raw = JSON.stringify({
"id": "String"
});
var requestOptions = {
method: 'GET',
headers: myHeaders,
body: raw,
redirect: 'follow'
};
fetch("https://api.cakes.com", requestOptions)
.then(response => response.text())
.then(result => console.log(result))
.catch(error => console.log('error', error));For more information about this endpoint, see setSolanaProgramAuthority.
var myHeaders = new Headers();
myHeaders.append("Accept", "application/json");
myHeaders.append("Content-Type", "application/json");
var raw = JSON.stringify({
"id": "String"
});
var requestOptions = {
method: 'GET',
headers: myHeaders,
body: raw,
redirect: 'follow'
};
fetch("https://api.cakes.com", requestOptions)
.then(response => response.text())
.then(result => console.log(result))
.catch(error => console.log('error', error));For more information about this endpoint, see deploySolanaProgram.
var myHeaders = new Headers();
myHeaders.append("Accept", "application/json");
myHeaders.append("Content-Type", "application/json");
var raw = JSON.stringify({
"id": "String"
});
var requestOptions = {
method: 'GET',
headers: myHeaders,
body: raw,
redirect: 'follow'
};
fetch("https://api.cakes.com", requestOptions)
.then(response => response.text())
.then(result => console.log(result))
.catch(error => console.log('error', error));For more information about this endpoint, see getSolanaMinimumBalanceForRentExemption.
var myHeaders = new Headers();
myHeaders.append("Accept", "application/json");
myHeaders.append("Content-Type", "application/json");
var raw = JSON.stringify({
"id": "String"
});
var requestOptions = {
method: 'GET',
headers: myHeaders,
body: raw,
redirect: 'follow'
};
fetch("https://api.cakes.com", requestOptions)
.then(response => response.text())
.then(result => console.log(result))
.catch(error => console.log('error', error));For more information about this endpoint, see getSolanaProgramAddressFromSeed.
var myHeaders = new Headers();
myHeaders.append("Accept", "application/json");
myHeaders.append("Content-Type", "application/json");
var raw = JSON.stringify({
"id": "String"
});
var requestOptions = {
method: 'GET',
headers: myHeaders,
body: raw,
redirect: 'follow'
};
fetch("https://api.cakes.com", requestOptions)
.then(response => response.text())
.then(result => console.log(result))
.catch(error => console.log('error', error));For more information about this endpoint, see closeSolanaBpfLoaderAccounts.