Fleet Rentals
The SRSLY program enables on–chain rental agreements for fleets in the Star Atlas ecosystem. It lets fleet owners create rental contracts and borrowers accept rentals, automate recurring payments via threads, and later cancel, close, or reset rentals. This guide provides a comprehensive walkthrough of how to set up your environment, load the program, and interact with key instructions using the updated IDL.
Program Details
Program Name:
srsly
Version:
1.0.0
(spec:0.1.0
)Program Address:
SRSLY1fq9TJqCk1gNSE7VZL2bztvTn9wm4VR8u8jMKT
Description: SRSLY is a rental contract system for Star Atlas. It enables fleet owners to define rental terms (e.g. rate, duration, payment frequency) and borrowers to accept rentals using in–game ATLAS tokens.
Token Used:
ATLAS
(ATLAS mint address:ATLASXmbPQxBUYbxPsV97usA3fPQYEqzQBUHgiFCUsXx
)
IDL Attachment:
Setting Up Your Environment
Before you begin, make sure you have the proper dependencies installed:
🔧 Required Versions
Anchor: 0.31.0
Solana CLI: 2.1.6
You can verify your versions with:
solana --version # Expected: solana-cli 2.1.6
anchor --version # Expected: anchor-cli 0.30.1
If you need updates, follow the standard update procedures for Anchor and Solana CLI.
Loading the SRSLY Program in a TypeScript Project
In your project, set up your Anchor provider and load the SRSLY program as follows.
1️⃣ Import Required Dependencies
In your TypeScript file, import the necessary modules:
import { Program, AnchorProvider, BN, setProvider, workspace } from "@coral-xyz/anchor";
import { PublicKey } from "@solana/web3.js";
import { Srsly } from "./srsly"; // Adjust the path to point to your IDL file
2️⃣ Set Up the Anchor Provider
Establish your Solana connection and wallet provider:
const provider = AnchorProvider.local();
setProvider(provider);
3️⃣ Load the SRSLY Program
Once the provider is set, load the SRSLY program from your local workspace:
const program = workspace.Srsly as Program<Srsly>;
console.log("SRSLY Program ID:", program.programId.toBase58());
This confirms that you have the correct program loaded.
Quick Start: Running Sample Scripts
Once your environment is set, you can run sample scripts. Below are two examples for fetching account data.
Example: Fetch a Rental Contract Account
Assume you have already derived the program–derived address (PDA) for the contract. For example:
const fleetPublicKey = new PublicKey("..."); // Replace with your fleet's public key
const [contractPDA] = PublicKey.findProgramAddressSync(
[Buffer.from("rental_contract"), fleetPublicKey.toBuffer()],
program.programId
);
async function getContract() {
const contract = await program.account.contractState.fetch(contractPDA);
console.log("Fleet:", contract.fleet.toBase58());
console.log("Rental Rate:", contract.rate.toString());
console.log("Owner:", contract.owner.toBase58());
}
getContract();
Example: Fetch a Rental State Account
Derive the rental state PDA using the contract PDA and borrower's key:
const borrowerPublicKey = new PublicKey("..."); // Replace with your borrower's public key
const [rentalStatePDA] = PublicKey.findProgramAddressSync(
[
Buffer.from("rental_state"),
contractPDA.toBuffer(),
borrowerPublicKey.toBuffer()
],
program.programId
);
async function getRentalState() {
const rental = await program.account.rentalState.fetch(rentalStatePDA);
console.log("Borrower:", rental.borrower.toBase58());
console.log("Rental Effective Rate:", rental.rate.toString());
console.log("Rental End Time:", rental.end_time.toString());
}
getRentalState();
Key Accounts & Structures
Rental Contract Account (contractState)
This account defines the overall rental contract parameters for a fleet. It stores the essential terms, fleet details, and owner information which govern the rental agreement.
Purpose:
• Establish terms for the rental (rental rate, duration limits, and payment frequency).
• Track the fleet being offered for rent and its ownership details.
• Maintain the current rental state (or indicate if no rental is active, via a default value such as the system program’s ID).
PDA Derivation:
The contract account is derived using the following seed:
• Seeds: [ "rental_contract", fleet.publicKey ]
For example, in TypeScript:
const [contractPDA] = PublicKey.findProgramAddressSync(
[Buffer.from("rental_contract"), fleetPublicKey.toBuffer()],
program.programId
);
Data Structure:
version
u8
The account version (typically set to 1).
to_close
bool
Flag indicating if the contract is scheduled for closure.
rate
u64
The rental price per period (converted from Stardust to ATLAS).
duration_min
u64
The minimum duration allowed for the rental session.
duration_max
u64
The maximum allowable rental duration.
payments_feq
PaymentFrequency
Enum representing the payment frequency (Daily)
fleet
pubkey
Public key representing the fleet asset being rented.
game_id
pubkey
Identifier for the associated game.
current_rental_state
pubkey
Tracks the PDA of the active rental state. Defaults to system program’s ID when free.
owner
pubkey
The fleet owner’s public key.
owner_token_account
pubkey
Owner’s token account used for receiving rental payments.
owner_profile
pubkey
The owner’s profile account from the Sage program.
bump
u8
The bump seed value from the PDA derivation.
–––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
Rental State Account (rentalState)
The Rental State account captures all details specific to an active rental session. It keeps track of payment details, session timings, cancellation status, and the thread that automates recurring payments.
Purpose:
• Record the active rental details including the borrower, effective payment rate, and the overall timeline of the rental.
• Coordinate with a recurring payment thread to automate the transfer of rental fees over the rental period.
PDA Derivation:
This account is derived using:
• Seeds: [ "rental_state", contractPDA, borrower.publicKey ]
For example, in TypeScript:
const [rentalStatePDA] = PublicKey.findProgramAddressSync(
[Buffer.from("rental_state"), contractPDA.toBuffer(), borrowerPublicKey.toBuffer()],
program.programId
);
Data Structure:
version
u8
The account version (typically 1).
borrower
pubkey
Public key of the user borrowing the fleet.
thread
pubkey
The public key of the thread account managing recurring payments for this rental.
contract
pubkey
The rental contract’s public key associated with this rental session.
owner_token_account
pubkey
The owner’s token account; payments are routed here.
rate
f64
The effective rental rate for this session (after fee adjustments).
start_time
i64
Unix timestamp when the rental session started.
end_time
i64
Unix timestamp when the rental session is scheduled to end.
cancelled
bool
Flag indicating whether the rental was cancelled before the agreed end time.
bump
u8
The PDA bump seed used during derivation.
–––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
These two accounts work together to support the full lifecycle of a rental: the contract establishes the rules and availability for renting a fleet, while the rental state captures each active session’s details, enabling automated payment processing and control via associated threads.
Feel free to modify the formatting in Notion as needed to match your style preferences.
Detailed Step–by–Step Instruction Guide
The following instructions detail how to work with the major functionalities of the SRSLY program using Anchor and the updated IDL.
1️⃣ Create a Rental Contract
This instruction creates a new rental contract and registers the fleet with defined terms.
Instruction: create_contract
create_contract
Key Accounts:
mint: (ATLAS mint account)
owner: (Signer – fleet owner)
owner_token_account: (Owner’s token account; often a PDA)
fleet: (Fleet account from Sage)
owner_profile: (Owner’s profile)
game_id: (Identifier for the game)
contract: (Writable PDA; derived using seeds
[ "rental_contract", fleet.publicKey ]
)rental_authority: (PDA using seed
"rental_authority"
)sage_program, token_program, system_program: (For CPI and token transfers)
Arguments:
rate: u64
– Rental price per periodduration_min: u64
– Minimum rental durationduration_max: u64
– Maximum rental durationpayments_feq: string
– Payment frequency (e.g.,@daily
)owner_key_index: u16
– Owner profile key index
Example Usage:
await program.methods.createContract(
new BN(5000), // rate (in ATLAS)
new BN(1), // minimum duration (days)
new BN(10), // maximum duration (days)
"@daily", // payment frequency
0 // owner key index
).accounts({
owner: ownerPublicKey,
fleet: fleetPublicKey,
owner_profile: ownerProfilePublicKey,
game_id: gameIdPublicKey,
mint: new PublicKey("ATLASXmbPQxBUYbxPsV97usA3fPQYEqzQBUHgiFCUsXx"),
owner_token_account: ownerTokenAccountPublicKey,
contract: contractPDA,
rental_authority: rentalAuthorityPDA,
sage_program: new PublicKey("sAgezwJpDb1aHvzNr3o24cKjsETmFEKghBEyJ1askDi"),
token_program: new PublicKey("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"),
system_program: new PublicKey("11111111111111111111111111111111")
}).rpc();
2️⃣ Accept a Rental
This call allows a borrower to accept a rental contract. It sets up a rental state, transfers tokens into an escrow account, and creates a recurring payment thread.
Instruction: accept_rental
accept_rental
Key Accounts:
mint: ATLAS mint (provided account)
borrower: Signer accepting the rental
borrower_profile & borrower_profile_faction: (Sage accounts)
borrower_token_account: (Writable, PDA derived from borrower and mint)
fleet: (Writable; must follow
sub_profile
rules)game_id, starbase, starbase_player: (Sage integration accounts)
contract: (Writable PDA from
[ "rental_contract", fleet ]
)rental_state: (Writable PDA from
[ "rental_state", contract, borrower ]
)rental_authority: PDA using seed
"rental_authority"
rental_token_account: (Escrow account for rental payments)
rental_thread: (Thread account for recurring payments)
fee_token_account: (For fee collection)
sage_program, antegen_program, token_program, associated_token_program, system_program: (For CPI and transfers)
Arguments:
amount: u64
– Payment amountduration: u64
– Duration (in days, etc.)
Example Usage:
await program.methods.acceptRental(
new BN(5000), // Payment amount in ATLAS
new BN(5) // Rental duration (5 days)
).accounts({
borrower: borrowerPublicKey,
fleet: fleetPublicKey,
game_id: gameIdPublicKey,
borrower_profile: borrowerProfilePublicKey,
borrower_profile_faction: borrowerProfileFactionPublicKey,
rental_state: rentalStatePDA,
mint: new PublicKey("ATLASXmbPQxBUYbxPsV97usA3fPQYEqzQBUHgiFCUsXx"),
rental_token_account: rentalTokenAccountPDA,
rental_thread: rentalThreadPublicKey,
sage_program: new PublicKey("sAgezwJpDb1aHvzNr3o24cKjsETmFEKghBEyJ1askDi"),
token_program: new PublicKey("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"),
antegen_program: new PublicKey("AgThdyi1P5RkVeZD2rQahTvs8HePJoGFFxKtvok5s2J1"),
associated_token_program: new PublicKey("ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"),
system_program: new PublicKey("11111111111111111111111111111111")
}).rpc();
3️⃣ Pay Rental
This instruction handles ongoing rental payments. It transfers tokens from the escrow to the owner, evaluates whether the rental term is complete, and—if needed—finalizes and closes or resets the rental.
Instruction: pay_rental
pay_rental
Key Accounts:
borrower & borrower_token_account: (For sending payment details)
owner & owner_token_account: (For receiving rental payments)
fleet, game_id, starbase, starbase_player: (Related Sage accounts)
contract: (Writable PDA for the rental contract)
rental_state: (Writable current rental state)
rental_authority: PDA for automated execution
rental_token_account: (Escrow account)
rental_thread: (Signer; thread account for payment scheduling)
sage_program, antegen_program, token_program: (For CPI and transfers)
Example Usage:
const response = await program.methods.payRental().accounts({
borrower: borrowerPublicKey,
borrower_token_account: borrowerTokenAccountPublicKey,
owner: ownerPublicKey,
owner_token_account: ownerTokenAccountPublicKey,
fleet: fleetPublicKey,
game_id: gameIdPublicKey,
starbase: starbasePublicKey,
starbase_player: starbasePlayerPublicKey,
contract: contractPDA,
rental_state: rentalStatePDA,
rental_authority: rentalAuthorityPDA,
rental_token_account: rentalTokenAccountPDA,
rental_thread: rentalThreadPublicKey,
sage_program: new PublicKey("sAgezwJpDb1aHvzNr3o24cKjsETmFEKghBEyJ1askDi"),
antegen_program: new PublicKey("AgThdyi1P5RkVeZD2rQahTvs8HePJoGFFxKtvok5s2J1"),
token_program: new PublicKey("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA")
}).rpc();
console.log("Thread Response:", response);
The returned value is a ThreadResponse that may include a new trigger or a close–to account if the rental session is finalized.
4️⃣ Cancel Rental
This instruction allows the borrower to cancel an active rental early. It updates the rental state (setting a cancellation flag and adjusting the end time) so that final payment logic can refund funds accordingly.
Instruction: cancel_rental
cancel_rental
Key Accounts:
borrower: (Signer requesting cancellation)
rental_thread: (Thread account associated with the rental)
contract: (Rental contract account)
rental_state: (Writable PDA representing the active rental)
Example Usage:
await program.methods.cancelRental().accounts({
borrower: borrowerPublicKey,
rental_thread: rentalThreadPublicKey,
contract: contractPDA,
rental_state: rentalStatePDA
}).rpc();
5️⃣ Close Rental
Close an active rental session by processing any remaining payments, deleting the recurring thread, and finalizing the rental state.
Instruction: close_rental
close_rental
Key Accounts:
borrower: (Signer)
borrower_token_account & owner_token_account: (For final token transfers)
contract: (Rental contract account)
rental_state: (Writable PDA for the active rental)
rental_token_account: (Escrow token account)
rental_authority: (PDA)
rental_thread: (Thread account)
antegen_program, token_program, system_program: (For final transfers and thread deletion)
Example Usage:
await program.methods.closeRental().accounts({
borrower: borrowerPublicKey,
borrower_token_account: borrowerTokenAccountPublicKey,
owner_token_account: ownerTokenAccountPublicKey,
contract: contractPDA,
rental_state: rentalStatePDA,
rental_token_account: rentalTokenAccountPDA,
rental_authority: rentalAuthorityPDA,
rental_thread: rentalThreadPublicKey,
antegen_program: new PublicKey("AgThdyi1P5RkVeZD2rQahTvs8HePJoGFFxKtvok5s2J1"),
token_program: new PublicKey("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"),
system_program: new PublicKey("11111111111111111111111111111111")
}).rpc();
6️⃣ Close Contract
Shut down a rental contract. If a rental is in progress, final payments are processed before closure. Otherwise, the contract is closed immediately.
Instruction: close_contract
close_contract
Key Accounts:
owner: (Signer, fleet owner)
contract: (Writable rental contract account)
owner_token_account, rental_token_account, borrower_token_account, rental_state: (Optional accounts if an active rental exists)
fleet, game_id, starbase, starbase_player: (Additional Sage accounts)
rental_authority: (PDA)
sage_program, token_program: (For CPI transfers)
Example Usage:
await program.methods.closeContract().accounts({
owner: ownerPublicKey,
contract: contractPDA,
sage_program: new PublicKey("sAgezwJpDb1aHvzNr3o24cKjsETmFEKghBEyJ1askDi"),
token_program: new PublicKey("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA")
}).rpc();
7️⃣ Reset Rental
Reset the rental state (for example, after an early cancellation or when cleaning up state). This call uses a Sage CPI call to invalidate the rental.
Instruction: reset_rental
reset_rental
Key Accounts:
fleet, game_id, starbase, starbase_player: (Writable or checked accounts for rental reset)
rental_state: (Must be empty – ensures no active rental state exists)
contract: (Writable rental contract account)
rental_authority: (PDA)
sage_program: (For CPI calls)
Example Usage:
await program.methods.resetRental().accounts({
fleet: fleetPublicKey,
game_id: gameIdPublicKey,
starbase: starbasePublicKey,
starbase_player: starbasePlayerPublicKey,
rental_state: rentalStatePublicKey, // Should be empty
contract: contractPDA,
rental_authority: rentalAuthorityPDA,
sage_program: new PublicKey("sAgezwJpDb1aHvzNr3o24cKjsETmFEKghBEyJ1askDi")
}).rpc();
PDA Derivation Summary
Many key accounts in the SRSLY program are derived using Program Derived Addresses (PDAs).
Rental Contract PDA
const [contractPDA] = PublicKey.findProgramAddressSync(
[Buffer.from("rental_contract"), fleetPublicKey.toBuffer()],
program.programId
);
Rental State PDA
const [rentalStatePDA] = PublicKey.findProgramAddressSync(
[Buffer.from("rental_state"), contractPDA.toBuffer(), borrowerPublicKey.toBuffer()],
program.programId
);
Rental Authority PDA
const [rentalAuthorityPDA] = PublicKey.findProgramAddressSync(
[Buffer.from("rental_authority")],
program.programId
);
Rental Token Account PDA Example
Often, the rental token escrow account is derived using a combination of the rental state, the token program, and the ATLAS mint:
const [rentalTokenAccountPDA] = PublicKey.findProgramAddressSync(
[
rentalStatePDA.toBuffer(),
new PublicKey("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA").toBuffer(),
new PublicKey("ATLASXmbPQxBUYbxPsV97usA3fPQYEqzQBUHgiFCUsXx").toBuffer()
],
new PublicKey("ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL")
);
Rental Payment Thread PDA
const [rentalThreadPDA] = PublicKey.findProgramAddressSync(
[rentalAuthorityPDA.toBuffer(), rentalStatePDA.toBuffer()],
new PublicKey("AgThdyi1P5RkVeZD2rQahTvs8HePJoGFFxKtvok5s2J1")
);
Program Errors
The SRSLY program uses custom error codes to enforce rules and validations throughout its instructions. Each error code provides a descriptive message that helps in diagnosing issues during contract interactions.
Error Code
Error Name
Description
6000
InvalidDurationMinimum
"Invalid duration minimum. Must be between 1 and the duration maximum."
6001
InvalidDurationMaximum
"Invalid duration maximum. Must be greater than or equal to the duration minimum."
6002
InvalidRateCalculation
"The contract rate multiplied by duration exceeds the payment amount."
6003
FleetAlreadyRented
"Fleet is already rented (sub_profile is not empty)."
6004
InvalidRate
"Contract rate must be greater than or equal to 0."
6005
InvalidPaymentFrequency
"Invalid payment frequency. Must be one of: @daily"
6006
InvalidSubProfileInvalidator
"Invalid sub_profile invalidator."
6007
InsufficientCancellationNotice
"Rental time remaining is less than minimum cancellation notice required."
6008
ContractClosed
"The contract is closed."
6009
DevOnlyFrequency
"This frequency is only allowed in development."
6010
ExpectedDailyFrequency
"Expected daily frequency but contract is set differently."
6011
InvalidThreadContext
"Thread has invalid context set."
6012
RentalIsActive
"Rental is still active."
6013
RentalStateExists
"Rental reset requires rental state to not exist."
Key Constants
These public keys are essential for interacting with the SRSLY rental program and related Solana programs:
Constant Name
Public Key
Description
SAGE_PROGRAM
SAGE2HAwep459SNq61LHvjxPk4pLPEJLoMETef7f7EE
Sage in-game asset control.
SRSLY_PROGRAM
SRSLY1fq9TJqCk1gNSE7VZL2bztvTn9wm4VR8u8jMKT
SRSLY rental program ID.
ANTIGEN_PROGRAM
AgThdyi1P5RkVeZD2rQahTvs8HePJoGFFxKtvok5s2J1
Thread automation framework.
SYSTEM_PROGRAM
11111111111111111111111111111111
Solana’s system program.
TOKEN_PROGRAM
TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA
SPL token program.
ASSOCIATED_TOKEN_PROGRAM
ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL
Creates associated token accounts.
FACTION_PROGRAM
pFACSRuobDmvfMKq1bAzwj27t6d2GJhSCHb1VcfnRmq
Manages player factions.
GAME_STATE
DeXGvdhyVUMSbmGWZtwFm5NM3q3TmRDKaAF3KgGx3dBJ
Sage global game state. (Will Change)
GAME_ID
GAMEzqJehF8yAnKiTARUuhZMvLvkZVAsCVri5vSfemLr
Unique game instance in Star Atlas.
ATLAS_MINT
ATLASXmbPQxBUYbxPsV97usA3fPQYEqzQBUHgiFCUsXx
Official ATLAS
token mint.
Final Remarks
Integration with Sage:
Many instructions (like accepting a rental and resetting) require interaction with Sage program accounts (such as the fleet’s
sub_profile
andsub_profile_invalidator
).Token Transfers & Escrow:
The program uses the Anchor SPL Token library. Account addresses are derived via a mix of constant seeds and account keys.
Happy Renting on Solana!
Ensure you review the full IDL (attached above) for all details and updates before integration.
Last updated
Was this helpful?