Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save snowsky/ac5dd5ad980f0d559a68c6ffbafc1d5a to your computer and use it in GitHub Desktop.

Select an option

Save snowsky/ac5dd5ad980f0d559a68c6ffbafc1d5a to your computer and use it in GitHub Desktop.
Solana Rent Share Client Example - basic agreement setup, initialization, and rental payment
import {
Connection,
Cluster,
Keypair,
clusterApiUrl,
PublicKey,
sendAndConfirmTransaction,
SystemProgram,
Transaction,
SYSVAR_RENT_PUBKEY,
TransactionInstruction,
} from "@solana/web3.js";
import BN from "bn.js";
import * as borsh from "borsh";
enum Instructions {
Initialize = 0,
PayRent,
TerminateEarly,
}
enum AgreementStatus {
Uninitialized = 0,
Active,
Completed,
Violated,
}
enum RentalDuration {
Months = 0,
}
export type ErrorData = {
error: {
name: string;
message: string;
};
};
export type RentShareAccountData = {
payeePublicKey: PublicKey;
payerPublicKey: PublicKey;
deposit: number;
rentAmount: number;
duration: number;
durationUnit: number;
remainingPayments: number;
};
class RentShareAccount {
status: AgreementStatus;
payeePublicKey: number[];
payerPublicKey: number[];
deposit = 0;
rentAmount = 0;
duration = 0;
durationUnit = 0;
remainingPayments = 0;
constructor(data: RentShareAccountData) {
this.status = AgreementStatus.Uninitialized;
if (data.payeePublicKey instanceof PublicKey) {
this.payeePublicKey = Array.from(data.payeePublicKey.toBytes());
} else {
this.payeePublicKey = Array.from(data.payeePublicKey);
}
if (data.payerPublicKey instanceof PublicKey) {
this.payerPublicKey = Array.from(data.payerPublicKey.toBytes());
} else {
this.payerPublicKey = Array.from(data.payerPublicKey);
}
this.deposit = data.deposit;
this.rentAmount = data.rentAmount;
this.duration = data.duration;
this.durationUnit = data.durationUnit;
this.remainingPayments = data.remainingPayments;
}
}
/**
* Borsh schema definition for rent share account data
*/
const RentShareDataSchema = new Map([
[
RentShareAccount,
{
kind: "struct",
fields: [
["status", "u8"],
["payeePublicKey", [32]],
["payerPublicKey", [32]],
["deposit", "u64"],
["rentAmount", "u64"],
["duration", "u64"],
["durationUnit", "u8"],
["remainingPayments", "u64"],
],
},
],
]);
/**
* The expected size of each rent share account.
*/
const RENT_SCHEMA_SIZE = borsh.serialize(
RentShareDataSchema,
new RentShareAccount({
payeePublicKey: PublicKey.default,
payerPublicKey: PublicKey.default,
deposit: 0,
rentAmount: 0,
duration: 0,
durationUnit: 0,
remainingPayments: 0,
})
).length;
/**
* Create Rent Share account if it doesn't already exist
*/
export async function createRentShareAccount(
accountOwner: Keypair,
seed: string
): Promise<PublicKey> {
const programId = new PublicKey(process.env.PROGRAM_ID || "");
const clusterUrl = clusterApiUrl(process.env.solanaEnvironment as Cluster);
const connection = new Connection(clusterUrl, "confirmed");
const rentAgreementPublicKey = await PublicKey.createWithSeed(
accountOwner.publicKey,
seed,
programId
);
const rentShareAccountInfo = await connection.getAccountInfo(
rentAgreementPublicKey
);
if (rentShareAccountInfo === null) {
const lamports = await connection.getMinimumBalanceForRentExemption(
RENT_SCHEMA_SIZE
);
console.log(
`Lamports for rent: ${lamports}. Schema size: ${RENT_SCHEMA_SIZE}`
);
const transaction = new Transaction().add(
SystemProgram.createAccountWithSeed({
fromPubkey: accountOwner.publicKey,
basePubkey: accountOwner.publicKey,
seed,
newAccountPubkey: rentAgreementPublicKey,
lamports,
space: RENT_SCHEMA_SIZE,
programId,
})
);
await sendAndConfirmTransaction(connection, transaction, [accountOwner]);
}
return rentAgreementPublicKey;
}
/**
* Initialize a rental agreement between a payer and payee specified in account data
*/
export async function initializeRentShareAccount(
rentAgreementPublicKey: PublicKey,
accountOwner: Keypair,
data: RentShareAccountData
): Promise<void> {
const programId = new PublicKey(process.env.PROGRAM_ID || "");
const clusterUrl = clusterApiUrl(process.env.solanaEnvironment as Cluster);
const connection = new Connection(clusterUrl, "confirmed");
const instruction = Instructions.Initialize;
const transactionInstruction = new TransactionInstruction({
keys: [
{ pubkey: rentAgreementPublicKey, isSigner: false, isWritable: true },
{ pubkey: SYSVAR_RENT_PUBKEY, isSigner: false, isWritable: false },
],
programId,
data: Buffer.from(
Uint8Array.of(
instruction,
...Array.from(data.payeePublicKey.toBytes()),
...Array.from(data.payerPublicKey.toBytes()),
...new BN(data.deposit).toArray("le", 8),
...new BN(data.rentAmount).toArray("le", 8),
...new BN(data.duration).toArray("le", 8),
data.durationUnit
)
),
});
const signature = await sendAndConfirmTransaction(
connection,
new Transaction().add(transactionInstruction),
[accountOwner]
);
}
/**
* Record a rent transaction between a payer and payee
*/
export async function recordRentPayment(
rentAgreementPublicKey: PublicKey,
payerPrivateKey: Keypair,
payeePrivateKey: Keypair,
rentAmount: number
): Promise<string> {
const programId = new PublicKey(process.env.PROGRAM_ID || "");
const clusterUrl = clusterApiUrl(process.env.solanaEnvironment as Cluster);
const connection = new Connection(clusterUrl, "confirmed");
const instruction = Instructions.PayRent;
const transactionInstruction = new TransactionInstruction({
keys: [
{ pubkey: rentAgreementPublicKey, isSigner: false, isWritable: true },
{ pubkey: payeePrivateKey.publicKey, isSigner: false, isWritable: true },
{ pubkey: payerPrivateKey.publicKey, isSigner: true, isWritable: true },
{ pubkey: SystemProgram.programId, isSigner: false, isWritable: true },
],
programId,
data: Buffer.from(
Uint8Array.of(instruction, ...new BN(rentAmount).toArray("le", 8))
),
});
const signature = await sendAndConfirmTransaction(
connection,
new Transaction().add(transactionInstruction),
[payerPrivateKey]
);
return signature;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment