SPL token lending scaffolding (#582)
* Scaffolding for spl-token-lending program * Scaffolding for TS client
This commit is contained in:
parent
5bd2e1c98c
commit
a4f09fe05e
|
@ -9,11 +9,20 @@ updates:
|
||||||
open-pull-requests-limit: 3
|
open-pull-requests-limit: 3
|
||||||
labels:
|
labels:
|
||||||
- "automerge"
|
- "automerge"
|
||||||
|
- package-ecosystem: npm
|
||||||
|
directory: "/token-lending/js"
|
||||||
|
schedule:
|
||||||
|
interval: daily
|
||||||
|
time: "02:00"
|
||||||
|
timezone: America/Los_Angeles
|
||||||
|
open-pull-requests-limit: 3
|
||||||
|
labels:
|
||||||
|
- "automerge"
|
||||||
- package-ecosystem: npm
|
- package-ecosystem: npm
|
||||||
directory: "/token-swap/js"
|
directory: "/token-swap/js"
|
||||||
schedule:
|
schedule:
|
||||||
interval: daily
|
interval: daily
|
||||||
time: "01:00"
|
time: "03:00"
|
||||||
timezone: America/Los_Angeles
|
timezone: America/Los_Angeles
|
||||||
open-pull-requests-limit: 3
|
open-pull-requests-limit: 3
|
||||||
labels:
|
labels:
|
||||||
|
@ -22,7 +31,7 @@ updates:
|
||||||
directory: "/"
|
directory: "/"
|
||||||
schedule:
|
schedule:
|
||||||
interval: daily
|
interval: daily
|
||||||
time: "01:00"
|
time: "04:00"
|
||||||
timezone: America/Los_Angeles
|
timezone: America/Los_Angeles
|
||||||
labels:
|
labels:
|
||||||
- "automerge"
|
- "automerge"
|
||||||
|
|
|
@ -2846,6 +2846,19 @@ dependencies = [
|
||||||
"spl-token 2.0.6",
|
"spl-token 2.0.6",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "spl-token-lending"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"arrayref",
|
||||||
|
"num-derive",
|
||||||
|
"num-traits",
|
||||||
|
"num_enum",
|
||||||
|
"rand",
|
||||||
|
"solana-sdk",
|
||||||
|
"thiserror",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "spl-token-swap"
|
name = "spl-token-swap"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|
|
@ -5,6 +5,7 @@ members = [
|
||||||
"memo/program",
|
"memo/program",
|
||||||
"themis/program_bn",
|
"themis/program_bn",
|
||||||
"themis/program_ristretto",
|
"themis/program_ristretto",
|
||||||
|
"token-lending/program",
|
||||||
"token-swap/program",
|
"token-swap/program",
|
||||||
"token/cli",
|
"token/cli",
|
||||||
"token/program",
|
"token/program",
|
||||||
|
|
16
ci/script.sh
16
ci/script.sh
|
@ -106,4 +106,20 @@ js_token_swap() {
|
||||||
}
|
}
|
||||||
_ js_token_swap
|
_ js_token_swap
|
||||||
|
|
||||||
|
# Test token-lending js bindings
|
||||||
|
js_token_lending() {
|
||||||
|
cd token-lending/js
|
||||||
|
time npm install || exit $?
|
||||||
|
time npm run lint || exit $?
|
||||||
|
time npm run build || exit $?
|
||||||
|
|
||||||
|
npm run cluster:localnet || exit $?
|
||||||
|
npm run localnet:down
|
||||||
|
npm run localnet:update || exit $?
|
||||||
|
npm run localnet:up || exit $?
|
||||||
|
time npm run start || exit $?
|
||||||
|
npm run localnet:down
|
||||||
|
}
|
||||||
|
_ js_token_lending
|
||||||
|
|
||||||
exit 0
|
exit 0
|
||||||
|
|
|
@ -23,6 +23,7 @@ if [[ -z $1 ]]; then
|
||||||
programs=(
|
programs=(
|
||||||
memo/program
|
memo/program
|
||||||
token/program
|
token/program
|
||||||
|
token-lending/program
|
||||||
token-swap/program
|
token-swap/program
|
||||||
)
|
)
|
||||||
else
|
else
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
# Lending program
|
||||||
|
|
||||||
|
A lending protocol for the Token program on the Solana blockchain inspired by Aave.
|
||||||
|
|
||||||
|
Full documentation will be made available at https://spl.solana.com in the future
|
||||||
|
|
||||||
|
Web3 bindings are available in the `./js` directory.
|
|
@ -0,0 +1,35 @@
|
||||||
|
{
|
||||||
|
"root": true,
|
||||||
|
"env": {
|
||||||
|
"node": true,
|
||||||
|
"browser": true
|
||||||
|
},
|
||||||
|
"extends": [
|
||||||
|
"eslint:recommended",
|
||||||
|
"plugin:@typescript-eslint/eslint-recommended",
|
||||||
|
"plugin:@typescript-eslint/recommended",
|
||||||
|
"plugin:@typescript-eslint/recommended-requiring-type-checking",
|
||||||
|
"prettier",
|
||||||
|
"prettier/@typescript-eslint"
|
||||||
|
],
|
||||||
|
"parser": "@typescript-eslint/parser",
|
||||||
|
"parserOptions": {
|
||||||
|
"ecmaVersion": 2020,
|
||||||
|
"parser": "babel-eslint",
|
||||||
|
"project": "./tsconfig.json",
|
||||||
|
"sourceType": "module"
|
||||||
|
},
|
||||||
|
"plugins": [
|
||||||
|
"@typescript-eslint",
|
||||||
|
"prettier"
|
||||||
|
],
|
||||||
|
"rules": {
|
||||||
|
"no-console": 0,
|
||||||
|
"semi": 0,
|
||||||
|
"template-curly-spacing": [
|
||||||
|
2,
|
||||||
|
"always"
|
||||||
|
],
|
||||||
|
"@typescript-eslint/no-explicit-any": 0
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
node_modules
|
||||||
|
coverage
|
||||||
|
.nyc_output
|
||||||
|
.DS_Store
|
||||||
|
*.log
|
||||||
|
.vscode
|
||||||
|
.idea
|
||||||
|
dist
|
||||||
|
compiled
|
||||||
|
.awcache
|
||||||
|
.rpt2_cache
|
||||||
|
docs
|
||||||
|
lib
|
|
@ -0,0 +1,69 @@
|
||||||
|
# Token-lending JavaScript API
|
||||||
|
|
||||||
|
The Token-lending JavaScript library comprises:
|
||||||
|
|
||||||
|
* A library to interact with the on-chain program
|
||||||
|
* A test client that exercises the program
|
||||||
|
* Scripts to facilitate building the program
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
First fetch the npm dependencies, including `@solana/web3.js`, by running:
|
||||||
|
```sh
|
||||||
|
$ npm install
|
||||||
|
```
|
||||||
|
|
||||||
|
### Select a Network
|
||||||
|
|
||||||
|
The client connects to a local Solana cluster by default.
|
||||||
|
|
||||||
|
To enable on-chain program logs, set the `RUST_LOG` environment variable:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ export RUST_LOG=solana_runtime::native_loader=trace,solana_runtime::system_instruction_processor=trace,solana_runtime::bank=debug,solana_bpf_loader=debug,solana_rbpf=debug
|
||||||
|
```
|
||||||
|
|
||||||
|
To start a local Solana cluster run:
|
||||||
|
```bash
|
||||||
|
$ npm run localnet:update
|
||||||
|
$ npm run localnet:up
|
||||||
|
```
|
||||||
|
|
||||||
|
Solana cluster logs are available with:
|
||||||
|
```bash
|
||||||
|
$ npm run localnet:logs
|
||||||
|
```
|
||||||
|
|
||||||
|
For more details on working with a local cluster, see the [full
|
||||||
|
instructions](https://github.com/solana-labs/solana-web3.js#local-network).
|
||||||
|
|
||||||
|
### Build the on-chain program
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ npm run build:program
|
||||||
|
```
|
||||||
|
|
||||||
|
### Run the test client
|
||||||
|
|
||||||
|
```sh
|
||||||
|
$ npm run start
|
||||||
|
```
|
||||||
|
|
||||||
|
## Pointing to a public Solana cluster
|
||||||
|
|
||||||
|
Solana maintains three public clusters:
|
||||||
|
- `devnet` - Development cluster
|
||||||
|
- `testnet` - Tour De Sol test cluster
|
||||||
|
- `mainnet-beta` - Main cluster
|
||||||
|
|
||||||
|
Use npm scripts to configure which cluster.
|
||||||
|
|
||||||
|
To point to `devnet`:
|
||||||
|
```bash
|
||||||
|
$ npm run cluster:devnet
|
||||||
|
```
|
||||||
|
|
||||||
|
To point back to the local cluster:
|
||||||
|
```bash
|
||||||
|
$ npm run cluster:localnet
|
||||||
|
```
|
|
@ -0,0 +1,20 @@
|
||||||
|
/**
|
||||||
|
* Exercises the token-lending program
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { loadPrograms } from "./token-lending-test";
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
// These test cases are designed to run sequentially and in the following order
|
||||||
|
console.log("Run test: loadPrograms");
|
||||||
|
await loadPrograms();
|
||||||
|
console.log("Success\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
main().then(
|
||||||
|
() => process.exit(),
|
||||||
|
(err) => {
|
||||||
|
console.error(err);
|
||||||
|
process.exit(-1);
|
||||||
|
}
|
||||||
|
);
|
|
@ -0,0 +1,97 @@
|
||||||
|
import fs from "mz/fs";
|
||||||
|
import {
|
||||||
|
Account,
|
||||||
|
Connection,
|
||||||
|
BpfLoader,
|
||||||
|
PublicKey,
|
||||||
|
BPF_LOADER_PROGRAM_ID,
|
||||||
|
} from "@solana/web3.js";
|
||||||
|
|
||||||
|
import { Store } from "../client/util/store";
|
||||||
|
import { newAccountWithLamports } from "../client/util/new-account-with-lamports";
|
||||||
|
import { url } from "../client/util/url";
|
||||||
|
|
||||||
|
let connection: Connection | undefined;
|
||||||
|
async function getConnection(): Promise<Connection> {
|
||||||
|
if (connection) return connection;
|
||||||
|
|
||||||
|
connection = new Connection(url, "recent");
|
||||||
|
const version = await connection.getVersion();
|
||||||
|
|
||||||
|
console.log("Connection to cluster established:", url, version);
|
||||||
|
return connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function loadPrograms(): Promise<void> {
|
||||||
|
const connection = await getConnection();
|
||||||
|
const [tokenProgramId, tokenSwapProgramId] = await GetPrograms(connection);
|
||||||
|
|
||||||
|
console.log("Token Program ID", tokenProgramId.toString());
|
||||||
|
console.log("Token-swap Program ID", tokenSwapProgramId.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadProgram(
|
||||||
|
connection: Connection,
|
||||||
|
path: string
|
||||||
|
): Promise<PublicKey> {
|
||||||
|
const data = await fs.readFile(path);
|
||||||
|
const { feeCalculator } = await connection.getRecentBlockhash();
|
||||||
|
|
||||||
|
const loaderCost =
|
||||||
|
feeCalculator.lamportsPerSignature *
|
||||||
|
BpfLoader.getMinNumSignatures(data.length);
|
||||||
|
const minAccountBalance = await connection.getMinimumBalanceForRentExemption(
|
||||||
|
0
|
||||||
|
);
|
||||||
|
const minExecutableBalance = await connection.getMinimumBalanceForRentExemption(
|
||||||
|
data.length
|
||||||
|
);
|
||||||
|
const balanceNeeded = minAccountBalance + loaderCost + minExecutableBalance;
|
||||||
|
|
||||||
|
const from = await newAccountWithLamports(connection, balanceNeeded);
|
||||||
|
const program_account = new Account();
|
||||||
|
console.log("Loading program:", path);
|
||||||
|
await BpfLoader.load(
|
||||||
|
connection,
|
||||||
|
from,
|
||||||
|
program_account,
|
||||||
|
data,
|
||||||
|
BPF_LOADER_PROGRAM_ID
|
||||||
|
);
|
||||||
|
return program_account.publicKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function GetPrograms(
|
||||||
|
connection: Connection
|
||||||
|
): Promise<[PublicKey, PublicKey]> {
|
||||||
|
const store = new Store();
|
||||||
|
let tokenProgramId = null;
|
||||||
|
let tokenLendingProgramId = null;
|
||||||
|
try {
|
||||||
|
const config = await store.load("config.json");
|
||||||
|
console.log("Using pre-loaded Token and Token-lending programs");
|
||||||
|
console.log(
|
||||||
|
" Note: To reload programs remove client/util/store/config.json"
|
||||||
|
);
|
||||||
|
if ("tokenProgramId" in config && "tokenLendingProgramId" in config) {
|
||||||
|
tokenProgramId = new PublicKey(config["tokenProgramId"]);
|
||||||
|
tokenLendingProgramId = new PublicKey(config["tokenLendingProgramId"]);
|
||||||
|
} else {
|
||||||
|
throw new Error("Program ids not found");
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
tokenProgramId = await loadProgram(
|
||||||
|
connection,
|
||||||
|
"../../target/bpfel-unknown-unknown/release/spl_token.so"
|
||||||
|
);
|
||||||
|
tokenLendingProgramId = await loadProgram(
|
||||||
|
connection,
|
||||||
|
"../../target/bpfel-unknown-unknown/release/spl_token_lending.so"
|
||||||
|
);
|
||||||
|
await store.save("config.json", {
|
||||||
|
tokenProgramId: tokenProgramId.toString(),
|
||||||
|
tokenSwapProgramId: tokenLendingProgramId.toString(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return [tokenProgramId, tokenLendingProgramId];
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
import { Connection } from "@solana/web3.js";
|
||||||
|
|
||||||
|
export class TokenLending {
|
||||||
|
connection: Connection;
|
||||||
|
|
||||||
|
constructor(connection: Connection) {
|
||||||
|
this.connection = connection;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
import { Account, Connection } from "@solana/web3.js";
|
||||||
|
|
||||||
|
export async function newAccountWithLamports(
|
||||||
|
connection: Connection,
|
||||||
|
lamports = 1000000
|
||||||
|
): Promise<Account> {
|
||||||
|
const account = new Account();
|
||||||
|
const signature = await connection.requestAirdrop(
|
||||||
|
account.publicKey,
|
||||||
|
lamports
|
||||||
|
);
|
||||||
|
await connection.confirmTransaction(signature);
|
||||||
|
return account;
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
// zzz
|
||||||
|
export function sleep(ms: number): Promise<void> {
|
||||||
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
/**
|
||||||
|
* Simple file-based datastore
|
||||||
|
*/
|
||||||
|
|
||||||
|
import path from "path";
|
||||||
|
import fs from "mz/fs";
|
||||||
|
import mkdirp from "mkdirp";
|
||||||
|
|
||||||
|
type Config = { [key: string]: string };
|
||||||
|
|
||||||
|
export class Store {
|
||||||
|
static getDir(): string {
|
||||||
|
return path.join(__dirname, "store");
|
||||||
|
}
|
||||||
|
|
||||||
|
async load(uri: string): Promise<Config> {
|
||||||
|
const filename = path.join(Store.getDir(), uri);
|
||||||
|
const data = await fs.readFile(filename, "utf8");
|
||||||
|
return JSON.parse(data) as Config;
|
||||||
|
}
|
||||||
|
|
||||||
|
async save(uri: string, config: Config): Promise<void> {
|
||||||
|
await mkdirp(Store.getDir());
|
||||||
|
const filename = path.join(Store.getDir(), uri);
|
||||||
|
await fs.writeFile(filename, JSON.stringify(config), "utf8");
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
// To connect to a public cluster, set `export LIVE=1` in your
|
||||||
|
// environment. By default, `LIVE=1` will connect to the devnet cluster.
|
||||||
|
|
||||||
|
import { clusterApiUrl, Cluster } from "@solana/web3.js";
|
||||||
|
import dotenv from "dotenv";
|
||||||
|
|
||||||
|
function chooseCluster(): Cluster | undefined {
|
||||||
|
dotenv.config();
|
||||||
|
if (!process.env.LIVE) return;
|
||||||
|
switch (process.env.CLUSTER) {
|
||||||
|
case "devnet":
|
||||||
|
case "testnet":
|
||||||
|
case "mainnet-beta": {
|
||||||
|
return process.env.CLUSTER;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (process.env.CLUSTER) {
|
||||||
|
throw new Error(
|
||||||
|
`Unknown cluster "${process.env.CLUSTER}", check the .env file`
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
throw new Error("CLUSTER is not specified, check the .env file");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const cluster = chooseCluster();
|
||||||
|
|
||||||
|
export const url =
|
||||||
|
process.env.RPC_URL ||
|
||||||
|
(process.env.LIVE ? clusterApiUrl(cluster, false) : "http://localhost:8899");
|
||||||
|
|
||||||
|
export const urlTls =
|
||||||
|
process.env.RPC_URL ||
|
||||||
|
(process.env.LIVE ? clusterApiUrl(cluster, true) : "http://localhost:8899");
|
||||||
|
|
||||||
|
export const walletUrl =
|
||||||
|
process.env.WALLET_URL || "https://solana-example-webwallet.herokuapp.com/";
|
|
@ -0,0 +1,2 @@
|
||||||
|
LIVE=1
|
||||||
|
CLUSTER=devnet
|
|
@ -0,0 +1,2 @@
|
||||||
|
LIVE=1
|
||||||
|
CLUSTER=mainnet-beta
|
|
@ -0,0 +1,2 @@
|
||||||
|
LIVE=1
|
||||||
|
CLUSTER=testnet
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,71 @@
|
||||||
|
{
|
||||||
|
"name": "@solana/spl-token-lending",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"description": "SPL Token Lending JavaScript API",
|
||||||
|
"license": "MIT",
|
||||||
|
"author": "Solana Maintainers <maintainers@solana.foundation>",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/solana-labs/solana-program-library"
|
||||||
|
},
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/solana-labs/solana-program-library/issues"
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"publishConfig": {
|
||||||
|
"access": "public"
|
||||||
|
},
|
||||||
|
"main": "lib/index.cjs.js",
|
||||||
|
"module": "lib/index.esm.js",
|
||||||
|
"types": "lib/index.d.ts",
|
||||||
|
"files": [
|
||||||
|
"lib"
|
||||||
|
],
|
||||||
|
"testnetDefaultChannel": "edge",
|
||||||
|
"scripts": {
|
||||||
|
"build": "rollup -c rollup.config.ts",
|
||||||
|
"build:program": "rm client/util/store/config.json; ../../do.sh build token-lending",
|
||||||
|
"start": "ts-node cli/main.ts",
|
||||||
|
"lint": "eslint --ext .ts {cli,client}/* && prettier --check \"{cli,client}/**/*.ts\"",
|
||||||
|
"lint:fix": "eslint --ext .ts {cli,client}/* --fix && prettier --write \"{cli,client}/**/*.ts\"",
|
||||||
|
"cluster:localnet": "rm -f .env",
|
||||||
|
"cluster:devnet": "cp cluster-devnet.env .env",
|
||||||
|
"cluster:testnet": "cp cluster-testnet.env .env",
|
||||||
|
"cluster:mainnet-beta": "cp cluster-mainnet-beta.env .env",
|
||||||
|
"localnet:update": "solana-localnet update",
|
||||||
|
"localnet:up": "rm client/util/store/config.json; set -x; solana-localnet down; set -e; solana-localnet up",
|
||||||
|
"localnet:down": "solana-localnet down",
|
||||||
|
"localnet:logs": "solana-localnet logs -f"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@solana/web3.js": "^0.78.4"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@rollup/plugin-commonjs": "^15.1.0",
|
||||||
|
"@tsconfig/recommended": "^1.0.1",
|
||||||
|
"@types/eslint": "^7.2.3",
|
||||||
|
"@types/eslint-plugin-prettier": "^3.1.0",
|
||||||
|
"@types/mkdirp": "^1.0.1",
|
||||||
|
"@types/mz": "^2.7.1",
|
||||||
|
"@types/node": "^14.11.5",
|
||||||
|
"@types/prettier": "^2.1.1",
|
||||||
|
"@types/rollup-plugin-json": "^3.0.2",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^4.4.0",
|
||||||
|
"@typescript-eslint/parser": "^4.4.0",
|
||||||
|
"dotenv": "^8.2.0",
|
||||||
|
"eslint": "^7.10.0",
|
||||||
|
"eslint-config-prettier": "^6.12.0",
|
||||||
|
"eslint-plugin-prettier": "^3.1.4",
|
||||||
|
"prettier": "^2.1.2",
|
||||||
|
"rollup": "^2.28.2",
|
||||||
|
"rollup-plugin-json": "^4.0.0",
|
||||||
|
"rollup-plugin-node-resolve": "^5.2.0",
|
||||||
|
"rollup-plugin-sourcemaps": "^0.6.2",
|
||||||
|
"rollup-plugin-typescript2": "^0.27.3",
|
||||||
|
"ts-node": "^9.0.0",
|
||||||
|
"typescript": "^4.0.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
import commonjs from '@rollup/plugin-commonjs'
|
||||||
|
import json from 'rollup-plugin-json'
|
||||||
|
import resolve from 'rollup-plugin-node-resolve'
|
||||||
|
import sourceMaps from 'rollup-plugin-sourcemaps'
|
||||||
|
import typescript from 'rollup-plugin-typescript2'
|
||||||
|
|
||||||
|
const pkg = require('./package.json')
|
||||||
|
|
||||||
|
export default {
|
||||||
|
input: `client/index.ts`,
|
||||||
|
output: [
|
||||||
|
{ file: pkg.main, format: 'cjs', sourcemap: true },
|
||||||
|
{ file: pkg.module, format: 'es', sourcemap: true }
|
||||||
|
],
|
||||||
|
external: [],
|
||||||
|
watch: {
|
||||||
|
include: 'src/**'
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
json(),
|
||||||
|
typescript({useTsconfigDeclarationDir: false}),
|
||||||
|
commonjs(),
|
||||||
|
resolve(),
|
||||||
|
sourceMaps()
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"extends": "@tsconfig/recommended/tsconfig.json",
|
||||||
|
"ts-node": {
|
||||||
|
"compilerOptions": {
|
||||||
|
"module": "commonjs"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compilerOptions": {
|
||||||
|
"declaration": true,
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"module": "es2015"
|
||||||
|
},
|
||||||
|
"include": ["cli/**/*", "client/**/*"],
|
||||||
|
"exclude": ["node_modules"]
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
|
||||||
|
# Note: This crate must be built using do.sh
|
||||||
|
|
||||||
|
[package]
|
||||||
|
name = "spl-token-lending"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "Solana Program Library Token Lending"
|
||||||
|
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||||
|
repository = "https://github.com/solana-labs/solana-program-library"
|
||||||
|
license = "Apache-2.0"
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
no-entrypoint = []
|
||||||
|
program = ["solana-sdk/program"]
|
||||||
|
default = ["solana-sdk/default"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
arrayref = "0.3.6"
|
||||||
|
num_enum = "0.5.1"
|
||||||
|
num-derive = "0.3"
|
||||||
|
num-traits = "0.2"
|
||||||
|
solana-sdk = { version = "1.3.14", default-features = false }
|
||||||
|
thiserror = "1.0"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
rand = { version = "0.7.0"}
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
crate-type = ["cdylib", "lib"]
|
|
@ -0,0 +1,2 @@
|
||||||
|
[target.bpfel-unknown-unknown.dependencies.std]
|
||||||
|
features = []
|
|
@ -0,0 +1,15 @@
|
||||||
|
language = "C"
|
||||||
|
header = "/* Autogenerated SPL Token-Lending program C Bindings */"
|
||||||
|
pragma_once = true
|
||||||
|
cpp_compat = true
|
||||||
|
line_length = 80
|
||||||
|
tab_width = 4
|
||||||
|
style = "both"
|
||||||
|
|
||||||
|
[export]
|
||||||
|
prefix = "TokenLending_"
|
||||||
|
include = ["LendingInstruction", "State"]
|
||||||
|
|
||||||
|
[parse]
|
||||||
|
parse_deps = true
|
||||||
|
include = ["solana-sdk"]
|
|
@ -0,0 +1,24 @@
|
||||||
|
//! Program entrypoint definitions
|
||||||
|
|
||||||
|
#![cfg(feature = "program")]
|
||||||
|
#![cfg(not(feature = "no-entrypoint"))]
|
||||||
|
|
||||||
|
use crate::{error::LendingError, processor::Processor};
|
||||||
|
use solana_sdk::{
|
||||||
|
account_info::AccountInfo, entrypoint, entrypoint::ProgramResult,
|
||||||
|
program_error::PrintProgramError, pubkey::Pubkey,
|
||||||
|
};
|
||||||
|
|
||||||
|
entrypoint!(process_instruction);
|
||||||
|
fn process_instruction<'a>(
|
||||||
|
program_id: &Pubkey,
|
||||||
|
accounts: &'a [AccountInfo<'a>],
|
||||||
|
instruction_data: &[u8],
|
||||||
|
) -> ProgramResult {
|
||||||
|
if let Err(error) = Processor::process(program_id, accounts, instruction_data) {
|
||||||
|
// catch the error so we can print it
|
||||||
|
error.print::<LendingError>();
|
||||||
|
return Err(error);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
//! Error types
|
||||||
|
|
||||||
|
use num_derive::FromPrimitive;
|
||||||
|
use solana_sdk::{decode_error::DecodeError, program_error::ProgramError};
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
/// Errors that may be returned by the TokenLending program.
|
||||||
|
#[derive(Clone, Debug, Eq, Error, FromPrimitive, PartialEq)]
|
||||||
|
pub enum LendingError {
|
||||||
|
/// The account cannot be initialized because it is already being used.
|
||||||
|
#[error("Lending account already in use")]
|
||||||
|
AlreadyInUse,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<LendingError> for ProgramError {
|
||||||
|
fn from(e: LendingError) -> Self {
|
||||||
|
ProgramError::Custom(e as u32)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> DecodeError<T> for LendingError {
|
||||||
|
fn type_of() -> &'static str {
|
||||||
|
"Lending Error"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
//! Instruction types
|
||||||
|
|
||||||
|
/// Instructions supported by the lending program.
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
pub enum LendingInstruction {
|
||||||
|
/// Initializes a new lending pool.
|
||||||
|
InitPool,
|
||||||
|
// InitReserve,
|
||||||
|
// Deposit,
|
||||||
|
// Withdraw,
|
||||||
|
// Borrow,
|
||||||
|
// Repay,
|
||||||
|
// Liquidate,
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
#![deny(missing_docs)]
|
||||||
|
|
||||||
|
//! A lending program for the Solana blockchain.
|
||||||
|
|
||||||
|
pub mod entrypoint;
|
||||||
|
pub mod error;
|
||||||
|
pub mod instruction;
|
||||||
|
pub mod processor;
|
||||||
|
pub mod state;
|
||||||
|
|
||||||
|
// Export current solana-sdk types for downstream users who may also be building with a different
|
||||||
|
// solana-sdk version
|
||||||
|
pub use solana_sdk;
|
||||||
|
|
||||||
|
solana_sdk::declare_id!("TokenLend1ng1111111111111111111111111111111");
|
|
@ -0,0 +1,35 @@
|
||||||
|
//! Program state processor
|
||||||
|
|
||||||
|
#![cfg(feature = "program")]
|
||||||
|
|
||||||
|
use crate::error::LendingError;
|
||||||
|
use num_traits::FromPrimitive;
|
||||||
|
use solana_sdk::{
|
||||||
|
account_info::AccountInfo, decode_error::DecodeError, entrypoint::ProgramResult, info,
|
||||||
|
program_error::PrintProgramError, pubkey::Pubkey,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Program state handler.
|
||||||
|
pub struct Processor {}
|
||||||
|
|
||||||
|
impl Processor {
|
||||||
|
/// Processes an instruction
|
||||||
|
pub fn process(
|
||||||
|
_program_id: &Pubkey,
|
||||||
|
_accounts: &[AccountInfo],
|
||||||
|
_input: &[u8],
|
||||||
|
) -> ProgramResult {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PrintProgramError for LendingError {
|
||||||
|
fn print<E>(&self)
|
||||||
|
where
|
||||||
|
E: 'static + std::error::Error + DecodeError<E> + PrintProgramError + FromPrimitive,
|
||||||
|
{
|
||||||
|
match self {
|
||||||
|
LendingError::AlreadyInUse => info!("Error: Lending account already in use"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
//! State types
|
||||||
|
|
||||||
|
/// Lending pool state
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Copy, Debug, Default, PartialEq)]
|
||||||
|
pub struct PoolState {}
|
||||||
|
|
||||||
|
/// Pool reserve state
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Copy, Debug, Default, PartialEq)]
|
||||||
|
pub struct ReserveState {}
|
||||||
|
|
||||||
|
/// Borrow obligation state
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Copy, Debug, Default, PartialEq)]
|
||||||
|
pub struct ObligationState {}
|
Loading…
Reference in New Issue