Initial commit from template
This commit is contained in:
23
.eslintrc.js
Normal file
23
.eslintrc.js
Normal file
@@ -0,0 +1,23 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
env: {
|
||||
es2020: true,
|
||||
node: true,
|
||||
},
|
||||
extends: [
|
||||
"eslint:recommended",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
],
|
||||
parser: "@typescript-eslint/parser",
|
||||
parserOptions: {
|
||||
ecmaVersion: 2020,
|
||||
sourceType: "module",
|
||||
project: "./tsconfig.json",
|
||||
},
|
||||
ignorePatterns: ["**/target/**", "node_modules"],
|
||||
rules: {
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
"@typescript-eslint/no-unused-vars": ["warn", { "argsIgnorePattern": "^_" }],
|
||||
"@typescript-eslint/ban-ts-comment": "off",
|
||||
},
|
||||
};
|
||||
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
target/
|
||||
*.so
|
||||
.DS_Store
|
||||
11
.stackclass/compile.sh
Normal file
11
.stackclass/compile.sh
Normal file
@@ -0,0 +1,11 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# This script is used to compile your program on StackClass
|
||||
#
|
||||
# This runs before .stackclass/run.sh
|
||||
#
|
||||
# Learn more: https://docs.stackclass.dev/challenges/program-interface
|
||||
|
||||
set -e # Exit on failure
|
||||
|
||||
cargo build --release
|
||||
11
.stackclass/run.sh
Normal file
11
.stackclass/run.sh
Normal file
@@ -0,0 +1,11 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# This script is used to run your program on StackClass
|
||||
#
|
||||
# This runs after .stackclass/compile.sh
|
||||
#
|
||||
# Learn more: https://docs.stackclass.dev/challenges/program-interface
|
||||
|
||||
set -e # Exit on failure
|
||||
|
||||
exec target/release/stackclass-solana-lending-program "$@"
|
||||
19
Anchor.toml
Normal file
19
Anchor.toml
Normal file
@@ -0,0 +1,19 @@
|
||||
[toolchain]
|
||||
anchor_version = "0.30.1"
|
||||
|
||||
[features]
|
||||
resolution = true
|
||||
skip-lint = false
|
||||
|
||||
[programs.localnet]
|
||||
lending = "LendZ1111111111111111111111111111111111111"
|
||||
|
||||
[registry]
|
||||
url = "https://api.apr.dev"
|
||||
|
||||
[provider]
|
||||
cluster = "Localnet"
|
||||
wallet = "~/.config/solana/id.json"
|
||||
|
||||
[scripts]
|
||||
test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.ts"
|
||||
3997
Cargo.lock
generated
Normal file
3997
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
26
Cargo.toml
Normal file
26
Cargo.toml
Normal file
@@ -0,0 +1,26 @@
|
||||
[workspace]
|
||||
members = [
|
||||
"programs/*"
|
||||
]
|
||||
resolver = "2"
|
||||
|
||||
[package]
|
||||
name = "stackclass-solana-lending-program"
|
||||
version = "0.1.0"
|
||||
authors = ["StackClass <hello@stackclass.dev>"]
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
regex = "1"
|
||||
|
||||
[profile.release]
|
||||
overflow-checks = true
|
||||
lto = "fat"
|
||||
codegen-units = 1
|
||||
|
||||
[profile.release.build-override]
|
||||
opt-level = 3
|
||||
incremental = false
|
||||
codegen-units = 1
|
||||
14
migrations/deploy.ts
Normal file
14
migrations/deploy.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
// Migrations are managed by Anchor
|
||||
// This file can be used for custom deployment scripts
|
||||
|
||||
import * as anchor from "@coral-xyz/anchor";
|
||||
|
||||
async function main() {
|
||||
console.log("Running deployment script...");
|
||||
// Custom deployment logic can be added here
|
||||
}
|
||||
|
||||
main().catch((error) => {
|
||||
console.error(error);
|
||||
process.exitCode = 1;
|
||||
});
|
||||
24
package.json
Normal file
24
package.json
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"name": "solana-lending",
|
||||
"version": "0.1.0",
|
||||
"description": "Solana lending program built with Anchor",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "anchor test",
|
||||
"lint": "eslint --ext .ts tests/",
|
||||
"build": "anchor build",
|
||||
"deploy": "anchor deploy"
|
||||
},
|
||||
"dependencies": {
|
||||
"@coral-xyz/anchor": "^0.30.0",
|
||||
"@solana/web3.js": "^1.87.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/mocha": "^10.0.0",
|
||||
"@types/node": "^20.0.0",
|
||||
"eslint": "^8.0.0",
|
||||
"mocha": "^10.0.0",
|
||||
"ts-mocha": "^10.0.0",
|
||||
"typescript": "^5.0.0"
|
||||
}
|
||||
}
|
||||
24
programs/lending-program/Cargo.toml
Normal file
24
programs/lending-program/Cargo.toml
Normal file
@@ -0,0 +1,24 @@
|
||||
[package]
|
||||
name = "lending-program"
|
||||
version = "0.1.0"
|
||||
description = "Created with Anchor"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib", "lib"]
|
||||
name = "lending_program"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
cpi = ["no-entrypoint"]
|
||||
no-entrypoint = []
|
||||
no-idl = []
|
||||
no-log-ix-name = []
|
||||
idl-build = ["anchor-lang/idl-build", "anchor-spl/idl-build"]
|
||||
|
||||
[dependencies]
|
||||
anchor-lang = { version="0.30.1", features=["init-if-needed"] }
|
||||
anchor-spl = "0.30.1"
|
||||
pyth-sdk-solana = "0.10.1"
|
||||
pyth-solana-receiver-sdk = "0.3.1"
|
||||
solana-program = "1.18.17"
|
||||
19
programs/lending-program/src/lib.rs
Normal file
19
programs/lending-program/src/lib.rs
Normal file
@@ -0,0 +1,19 @@
|
||||
use anchor_lang::prelude::*;
|
||||
|
||||
declare_id!("LendZ1111111111111111111111111111111111111");
|
||||
|
||||
#[program]
|
||||
pub mod lending_program {
|
||||
use super::*;
|
||||
|
||||
pub fn initialize(ctx: Context<Initialize>) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Accounts)]
|
||||
pub struct Initialize<'info> {
|
||||
#[account(mut)]
|
||||
pub user: Signer<'info>,
|
||||
pub system_program: Program<'info, System>,
|
||||
}
|
||||
196
src/main.rs
Normal file
196
src/main.rs
Normal file
@@ -0,0 +1,196 @@
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
use std::env;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
struct ProgramInfo {
|
||||
program_id: String,
|
||||
instructions: Vec<InstructionInfo>,
|
||||
accounts: Vec<AccountInfo>,
|
||||
errors: Vec<ErrorInfo>,
|
||||
structs: Vec<StructInfo>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
struct InstructionInfo {
|
||||
name: String,
|
||||
arguments: Vec<ArgumentInfo>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
struct ArgumentInfo {
|
||||
name: String,
|
||||
type_name: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
struct AccountInfo {
|
||||
name: String,
|
||||
fields: Vec<FieldInfo>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
struct FieldInfo {
|
||||
name: String,
|
||||
type_name: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
struct ErrorInfo {
|
||||
name: String,
|
||||
code: u32,
|
||||
message: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
struct StructInfo {
|
||||
name: String,
|
||||
fields: Vec<FieldInfo>,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let args: Vec<String> = env::args().collect();
|
||||
|
||||
if args.len() > 1 && args[1] == "dump_info" {
|
||||
dump_program_info();
|
||||
} else {
|
||||
// Default behavior: do nothing
|
||||
println!("Solana Lending Program - Use 'dump_info' command to export program definition");
|
||||
}
|
||||
}
|
||||
|
||||
fn dump_program_info() {
|
||||
let project_root = env::current_dir().unwrap_or_else(|_| PathBuf::from("."));
|
||||
let lib_path = project_root.join("programs/lending-program/src/lib.rs");
|
||||
|
||||
let lib_content = fs::read_to_string(&lib_path).unwrap_or_else(|_| {
|
||||
eprintln!("Warning: Could not read lib.rs at {:?}", lib_path);
|
||||
String::new()
|
||||
});
|
||||
|
||||
let program_info = parse_program_info(&lib_content);
|
||||
|
||||
println!("{}", serde_json::to_string_pretty(&program_info).unwrap());
|
||||
}
|
||||
|
||||
fn parse_program_info(content: &str) -> ProgramInfo {
|
||||
let mut program_info = ProgramInfo {
|
||||
program_id: String::new(),
|
||||
instructions: Vec::new(),
|
||||
accounts: Vec::new(),
|
||||
errors: Vec::new(),
|
||||
structs: Vec::new(),
|
||||
};
|
||||
|
||||
// Parse program_id
|
||||
if let Some(captures) = regex::Regex::new(r#"declare_id!\("([^"]+)"\)"#).unwrap().captures(content) {
|
||||
if let Some(id) = captures.get(1) {
|
||||
program_info.program_id = id.as_str().to_string();
|
||||
}
|
||||
}
|
||||
|
||||
// Parse instructions (pub fn)
|
||||
let instruction_re = regex::Regex::new(r#"pub fn (\w+)\(ctx: Context<([^>]+)>(?:, ([^)]+))?\)"#).unwrap();
|
||||
for caps in instruction_re.captures_iter(content) {
|
||||
let name = caps.get(1).map(|m| m.as_str().to_string()).unwrap_or_default();
|
||||
let _context = caps.get(2).map(|m| m.as_str().to_string()).unwrap_or_default();
|
||||
let args_str = caps.get(3).map(|m| m.as_str().to_string()).unwrap_or_default();
|
||||
|
||||
let mut arguments = Vec::new();
|
||||
if !args_str.is_empty() {
|
||||
for arg in args_str.split(',') {
|
||||
let arg = arg.trim();
|
||||
if let Some((name_part, type_part)) = arg.split_once(':') {
|
||||
arguments.push(ArgumentInfo {
|
||||
name: name_part.trim().to_string(),
|
||||
type_name: type_part.trim().to_string(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
program_info.instructions.push(InstructionInfo {
|
||||
name,
|
||||
arguments,
|
||||
});
|
||||
}
|
||||
|
||||
// Parse account structures (#[derive(Accounts)] pub struct)
|
||||
let account_re = regex::Regex::new(r#"#\[derive\(Accounts\)\]\s+pub struct (\w+)<[^>]*>\s*\{([^}]+)\}"#).unwrap();
|
||||
for caps in account_re.captures_iter(content) {
|
||||
let name = caps.get(1).map(|m| m.as_str().to_string()).unwrap_or_default();
|
||||
let fields_str = caps.get(2).map(|m| m.as_str().to_string()).unwrap_or_default();
|
||||
|
||||
let mut fields = Vec::new();
|
||||
let mut current_field = String::new();
|
||||
|
||||
for line in fields_str.lines() {
|
||||
let line = line.trim();
|
||||
if line.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If it's an attribute line, continue accumulating
|
||||
if line.starts_with('#') {
|
||||
current_field.push_str(line);
|
||||
current_field.push(' ');
|
||||
} else {
|
||||
// If it's a field definition line
|
||||
if let Some((name_part, type_part)) = line.split_once(':') {
|
||||
let field_name = name_part.split_whitespace().last().unwrap_or("").to_string();
|
||||
fields.push(FieldInfo {
|
||||
name: field_name,
|
||||
type_name: type_part.trim().to_string(),
|
||||
});
|
||||
}
|
||||
current_field.clear();
|
||||
}
|
||||
}
|
||||
|
||||
program_info.accounts.push(AccountInfo { name, fields });
|
||||
}
|
||||
|
||||
// Parse errors (pub enum Error)
|
||||
let error_re = regex::Regex::new(r#"pub enum Error\s*\{([^}]+)\}"#).unwrap();
|
||||
if let Some(caps) = error_re.captures(content) {
|
||||
let errors_str = caps.get(1).map(|m| m.as_str().to_string()).unwrap_or_default();
|
||||
for (idx, line) in errors_str.lines().enumerate() {
|
||||
let line = line.trim();
|
||||
if line.is_empty() || line.starts_with('#') {
|
||||
continue;
|
||||
}
|
||||
let name = line.split_whitespace().next().unwrap_or("").to_string();
|
||||
program_info.errors.push(ErrorInfo {
|
||||
name: name.clone(),
|
||||
code: idx as u32 + 6000,
|
||||
message: name,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Parse regular structs (pub struct)
|
||||
let struct_re = regex::Regex::new(r#"pub struct (\w+)\s*\{([^}]+)\}"#).unwrap();
|
||||
for caps in struct_re.captures_iter(content) {
|
||||
let name = caps.get(1).map(|m| m.as_str().to_string()).unwrap_or_default();
|
||||
let fields_str = caps.get(2).map(|m| m.as_str().to_string()).unwrap_or_default();
|
||||
|
||||
let mut fields = Vec::new();
|
||||
for line in fields_str.lines() {
|
||||
let line = line.trim();
|
||||
if line.is_empty() || line.starts_with('#') {
|
||||
continue;
|
||||
}
|
||||
if let Some((name_part, type_part)) = line.split_once(':') {
|
||||
fields.push(FieldInfo {
|
||||
name: name_part.trim().to_string(),
|
||||
type_name: type_part.trim().to_string(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
program_info.structs.push(StructInfo { name, fields });
|
||||
}
|
||||
|
||||
program_info
|
||||
}
|
||||
18
stackclass.yml
Normal file
18
stackclass.yml
Normal file
@@ -0,0 +1,18 @@
|
||||
# Set this to true if you want debug logs.
|
||||
#
|
||||
# These can be VERY verbose, so we suggest turning them off
|
||||
# unless you really need them.
|
||||
debug: false
|
||||
|
||||
# Use this to change the Rust version used to run your code
|
||||
# on StackClass.
|
||||
#
|
||||
# Available versions: rust-1.87
|
||||
language_pack: rust-1.87
|
||||
|
||||
# The executable required to build and run the this project,
|
||||
# along with its minimum required version.
|
||||
required_executable: cargo (1.87)
|
||||
|
||||
# The main source file that users can edit for this project.
|
||||
user_editable_file: programs/lending-program/src/lib.rs
|
||||
42
test_cases.json
Normal file
42
test_cases.json
Normal file
@@ -0,0 +1,42 @@
|
||||
[
|
||||
{"slug": "be1", "title": "Environment Setup", "log_prefix": "env", "category": "base", "description": "Setup development environment"},
|
||||
{"slug": "rs2", "title": "Rust Basics", "log_prefix": "rust", "category": "base", "description": "Learn Rust fundamentals"},
|
||||
{"slug": "sm3", "title": "Solana Model", "log_prefix": "solana", "category": "base", "description": "Understand Solana program model"},
|
||||
{"slug": "at4", "title": "Anchor Try", "log_prefix": "anchor", "category": "base", "description": "Try Anchor framework"},
|
||||
{"slug": "st5", "title": "SPL Token Basics", "log_prefix": "token", "category": "base", "description": "Learn SPL Token basics"},
|
||||
{"slug": "cp6", "title": "Basic Deposit", "log_prefix": "deposit", "category": "base", "description": "Implement basic deposit function"},
|
||||
{"slug": "tt7", "title": "Basic Withdraw", "log_prefix": "withdraw", "category": "base", "description": "Implement basic withdraw function"},
|
||||
{"slug": "pa1", "title": "PDA Concept", "log_prefix": "pda1", "category": "pda", "description": "Learn PDA concept"},
|
||||
{"slug": "pa2", "title": "PDA Derivation", "log_prefix": "pda2", "category": "pda", "description": "Implement PDA derivation"},
|
||||
{"slug": "pa3", "title": "Bump Seeds", "log_prefix": "pda3", "category": "pda", "description": "Use bump seeds with PDAs"},
|
||||
{"slug": "pa4", "title": "PDA Practice", "log_prefix": "pda4", "category": "pda", "description": "Practice PDA implementation"},
|
||||
{"slug": "tr1", "title": "Treasury Intro", "log_prefix": "tr1", "category": "treasury", "description": "Introduction to treasury"},
|
||||
{"slug": "tr2", "title": "Treasury Creation", "log_prefix": "tr2", "category": "treasury", "description": "Create treasury account"},
|
||||
{"slug": "tr3", "title": "Treasury Security", "log_prefix": "tr3", "category": "treasury", "description": "Implement treasury security"},
|
||||
{"slug": "tr4", "title": "Treasury Practice", "log_prefix": "tr4", "category": "treasury", "description": "Practice treasury implementation"},
|
||||
{"slug": "as1", "title": "Bank Account", "log_prefix": "as1", "category": "account_structure", "description": "Define bank account struct"},
|
||||
{"slug": "as2", "title": "User Account", "log_prefix": "as2", "category": "account_structure", "description": "Define user account struct"},
|
||||
{"slug": "as3", "title": "Account Space", "log_prefix": "as3", "category": "account_structure", "description": "Calculate account space"},
|
||||
{"slug": "as4", "title": "Account Practice", "log_prefix": "as4", "category": "account_structure", "description": "Practice account implementation"},
|
||||
{"slug": "lc1", "title": "Borrow Basics", "log_prefix": "lc1", "category": "lending_core", "description": "Implement borrow function"},
|
||||
{"slug": "lc2", "title": "Repay Basics", "log_prefix": "lc2", "category": "lending_core", "description": "Implement repay function"},
|
||||
{"slug": "lc3", "title": "LTV Calculation", "log_prefix": "lc3", "category": "lending_core", "description": "Calculate LTV and health factor"},
|
||||
{"slug": "lc4", "title": "Core Practice", "log_prefix": "lc4", "category": "lending_core", "description": "Practice core lending functions"},
|
||||
{"slug": "or1", "title": "Oracle Concept", "log_prefix": "or1", "category": "oracle", "description": "Learn oracle concept"},
|
||||
{"slug": "or2", "title": "Pyth Integration", "log_prefix": "or2", "category": "oracle", "description": "Integrate with Pyth"},
|
||||
{"slug": "or3", "title": "Price Fetching", "log_prefix": "or3", "category": "oracle", "description": "Fetch price from oracle"},
|
||||
{"slug": "or4", "title": "Oracle Practice", "log_prefix": "or4", "category": "oracle", "description": "Practice oracle implementation"},
|
||||
{"slug": "li1", "title": "Health Factor", "log_prefix": "li1", "category": "liquidation", "description": "Calculate health factor"},
|
||||
{"slug": "li2", "title": "Liquidation Trigger", "log_prefix": "li2", "category": "liquidation", "description": "Trigger liquidation on low health"},
|
||||
{"slug": "li3", "title": "Liquidation Process", "log_prefix": "li3", "category": "liquidation", "description": "Implement liquidation process"},
|
||||
{"slug": "li4", "title": "Liquidation Bonus", "log_prefix": "li4", "category": "liquidation", "description": "Add liquidation bonus"},
|
||||
{"slug": "li5", "title": "Liquidation Practice", "log_prefix": "li5", "category": "liquidation", "description": "Practice liquidation"},
|
||||
{"slug": "in1", "title": "Interest Basics", "log_prefix": "in1", "category": "interest", "description": "Implement interest calculation"},
|
||||
{"slug": "in2", "title": "Accrued Interest", "log_prefix": "in2", "category": "interest", "description": "Track accrued interest"},
|
||||
{"slug": "in3", "title": "Rate Models", "log_prefix": "in3", "category": "interest", "description": "Implement interest rate models"},
|
||||
{"slug": "in4", "title": "Interest Practice", "log_prefix": "in4", "category": "interest", "description": "Practice interest implementation"},
|
||||
{"slug": "se1", "title": "Common Vulnerabilities", "log_prefix": "se1", "category": "security", "description": "Handle common vulnerabilities"},
|
||||
{"slug": "se2", "title": "Reentrancy Protection", "log_prefix": "se2", "category": "security", "description": "Implement reentrancy guard"},
|
||||
{"slug": "se3", "title": "Account Validation", "log_prefix": "se3", "category": "security", "description": "Validate accounts properly"},
|
||||
{"slug": "se4", "title": "Security Practice", "log_prefix": "se4", "category": "security", "description": "Practice security patterns"}
|
||||
]
|
||||
34
tests/lending.ts
Normal file
34
tests/lending.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import * as anchor from "@coral-xyz/anchor";
|
||||
import { Program } from "@coral-xyz/anchor";
|
||||
import { Lending } from "../target/types/lending";
|
||||
|
||||
describe("lending", () => {
|
||||
const provider = anchor.AnchorProvider.env();
|
||||
anchor.setProvider(provider);
|
||||
|
||||
const program = anchor.workspace.Lending as Program<Lending>;
|
||||
|
||||
it("Initialize bank", async () => {
|
||||
// Test initialization
|
||||
});
|
||||
|
||||
it("Initialize user", async () => {
|
||||
// Test user initialization
|
||||
});
|
||||
|
||||
it("Deposit tokens", async () => {
|
||||
// Test deposit functionality
|
||||
});
|
||||
|
||||
it("Withdraw tokens", async () => {
|
||||
// Test withdraw functionality
|
||||
});
|
||||
|
||||
it("Borrow tokens", async () => {
|
||||
// Test borrow functionality
|
||||
});
|
||||
|
||||
it("Repay loan", async () => {
|
||||
// Test repay functionality
|
||||
});
|
||||
});
|
||||
14
tsconfig.json
Normal file
14
tsconfig.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"types": ["mocha"],
|
||||
"lib": ["esnext"],
|
||||
"module": "commonjs",
|
||||
"target": "es2020",
|
||||
"moduleResolution": "node",
|
||||
"esModuleInterop": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
"resolveJsonModule": true
|
||||
}
|
||||
}
|
||||
24
your_program.sh
Normal file
24
your_program.sh
Normal file
@@ -0,0 +1,24 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Use this script to run your program LOCALLY.
|
||||
#
|
||||
# Note: Changing this script WILL NOT affect how StackClass runs your program.
|
||||
#
|
||||
# Learn more: https://docs.stackclass.dev/challenges/program-interface
|
||||
|
||||
set -e # Exit early if any commands fail
|
||||
|
||||
# Copied from .stackclass/compile.sh
|
||||
#
|
||||
# - Edit this to change how your program compiles locally
|
||||
# - Edit .stackclass/compile.sh to change how your program compiles remotely
|
||||
(
|
||||
cd "$(dirname "$0")" # Ensure compile steps are run within the repository directory
|
||||
cargo build --release
|
||||
)
|
||||
|
||||
# Copied from .stackclass/run.sh
|
||||
#
|
||||
# - Edit this to change how your program runs locally
|
||||
# - Edit .stackclass/run.sh to change how your program runs remotely
|
||||
exec target/release/stackclass-solana-lending-program "$@"
|
||||
Reference in New Issue
Block a user