Skip to content

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:

srsly (1).json


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:

bash
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:

tsx
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:

tsx
const provider = AnchorProvider.local();
setProvider(provider);

3️⃣ Load the SRSLY Program

Once the provider is set, load the SRSLY program from your local workspace:

tsx
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:

tsx
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:

tsx
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:

tsx
const [contractPDA] = PublicKey.findProgramAddressSync(
    [Buffer.from("rental_contract"), fleetPublicKey.toBuffer()],
    program.programId
  );

Data Structure:

Field NameTypeDescription
versionu8The account version (typically set to 1).
to_closeboolFlag indicating if the contract is scheduled for closure.
rateu64The rental price per period (converted from Stardust to ATLAS).
duration_minu64The minimum duration allowed for the rental session.
duration_maxu64The maximum allowable rental duration.
payments_feqPaymentFrequencyEnum representing the payment frequency (Daily)
fleetpubkeyPublic key representing the fleet asset being rented.
game_idpubkeyIdentifier for the associated game.
current_rental_statepubkeyTracks the PDA of the active rental state. Defaults to system program’s ID when free.
ownerpubkeyThe fleet owner’s public key.
owner_token_accountpubkeyOwner’s token account used for receiving rental payments.
owner_profilepubkeyThe owner’s profile account from the Sage program.
bumpu8The 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:

tsx
const [rentalStatePDA] = PublicKey.findProgramAddressSync(
    [Buffer.from("rental_state"), contractPDA.toBuffer(), borrowerPublicKey.toBuffer()],
    program.programId
  );

Data Structure:

Field NameTypeDescription
versionu8The account version (typically 1).
borrowerpubkeyPublic key of the user borrowing the fleet.
threadpubkeyThe public key of the thread account managing recurring payments for this rental.
contractpubkeyThe rental contract’s public key associated with this rental session.
owner_token_accountpubkeyThe owner’s token account; payments are routed here.
ratef64The effective rental rate for this session (after fee adjustments).
start_timei64Unix timestamp when the rental session started.
end_timei64Unix timestamp when the rental session is scheduled to end.
cancelledboolFlag indicating whether the rental was cancelled before the agreed end time.
bumpu8The 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

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 period
  • duration_min: u64 – Minimum rental duration
  • duration_max: u64 – Maximum rental duration
  • payments_feq: string – Payment frequency (e.g., @daily)
  • owner_key_index: u16 – Owner profile key index

Example Usage:

tsx
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

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 amount
  • duration: u64 – Duration (in days, etc.)

Example Usage:

tsx
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

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:

tsx
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

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:

tsx
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

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:

tsx
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

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:

tsx
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

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:

tsx
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

tsx
const [contractPDA] = PublicKey.findProgramAddressSync(
  [Buffer.from("rental_contract"), fleetPublicKey.toBuffer()],
  program.programId
);

Rental State PDA

tsx
const [rentalStatePDA] = PublicKey.findProgramAddressSync(
  [Buffer.from("rental_state"), contractPDA.toBuffer(), borrowerPublicKey.toBuffer()],
  program.programId
);

Rental Authority PDA

tsx
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:

tsx
const [rentalTokenAccountPDA] = PublicKey.findProgramAddressSync(
  [
    rentalStatePDA.toBuffer(),
    new PublicKey("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA").toBuffer(),
    new PublicKey("ATLASXmbPQxBUYbxPsV97usA3fPQYEqzQBUHgiFCUsXx").toBuffer()
  ],
  new PublicKey("ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL")
);

Rental Payment Thread PDA

tsx
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 CodeError NameDescription
6000InvalidDurationMinimum"Invalid duration minimum. Must be between 1 and the duration maximum."
6001InvalidDurationMaximum"Invalid duration maximum. Must be greater than or equal to the duration minimum."
6002InvalidRateCalculation"The contract rate multiplied by duration exceeds the payment amount."
6003FleetAlreadyRented"Fleet is already rented (sub_profile is not empty)."
6004InvalidRate"Contract rate must be greater than or equal to 0."
6005InvalidPaymentFrequency"Invalid payment frequency. Must be one of: @daily"
6006InvalidSubProfileInvalidator"Invalid sub_profile invalidator."
6007InsufficientCancellationNotice"Rental time remaining is less than minimum cancellation notice required."
6008ContractClosed"The contract is closed."
6009DevOnlyFrequency"This frequency is only allowed in development."
6010ExpectedDailyFrequency"Expected daily frequency but contract is set differently."
6011InvalidThreadContext"Thread has invalid context set."
6012RentalIsActive"Rental is still active."
6013RentalStateExists"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 NamePublic KeyDescription
SAGE_PROGRAMSAGE2HAwep459SNq61LHvjxPk4pLPEJLoMETef7f7EESage in-game asset control.
SRSLY_PROGRAMSRSLY1fq9TJqCk1gNSE7VZL2bztvTn9wm4VR8u8jMKTSRSLY rental program ID.
ANTIGEN_PROGRAMAgThdyi1P5RkVeZD2rQahTvs8HePJoGFFxKtvok5s2J1Thread automation framework.
SYSTEM_PROGRAM11111111111111111111111111111111Solana’s system program.
TOKEN_PROGRAMTokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DASPL token program.
ASSOCIATED_TOKEN_PROGRAMATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knLCreates associated token accounts.
FACTION_PROGRAMpFACSRuobDmvfMKq1bAzwj27t6d2GJhSCHb1VcfnRmqManages player factions.
GAME_STATEDeXGvdhyVUMSbmGWZtwFm5NM3q3TmRDKaAF3KgGx3dBJSage global game state. (Will Change)
GAME_IDGAMEzqJehF8yAnKiTARUuhZMvLvkZVAsCVri5vSfemLrUnique game instance in Star Atlas.
ATLAS_MINTATLASXmbPQxBUYbxPsV97usA3fPQYEqzQBUHgiFCUsXxOfficial 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 and sub_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.

Official builder docs for the Star Atlas ecosystem.