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:

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:

Field Name
Type
Description

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:

Field Name
Type
Description

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

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:

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:

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:

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:

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:

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:

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:

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 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.

Last updated

Was this helpful?