@liens/cli 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +42 -12
- package/dist/bin.js +3 -1
- package/dist/bin.js.map +1 -1
- package/dist/commands/receipts.d.ts +3 -0
- package/dist/commands/receipts.d.ts.map +1 -0
- package/dist/commands/receipts.js +109 -0
- package/dist/commands/receipts.js.map +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,18 +1,48 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @liens/cli
|
|
2
|
+
|
|
3
|
+
The command-line half of the same toolkit the LIEN Designer runs in the browser. Both call `@liens/sdk`; both fold a Composition through the same Rust `hook-runtime` the Anchor executor links. The number `lien` prints is the number the chain runs.
|
|
2
4
|
|
|
3
5
|
```
|
|
4
|
-
npm i -g
|
|
6
|
+
npm i -g @liens/cli
|
|
7
|
+
lien list # the six standard knots
|
|
8
|
+
lien create slip-knot --pool SOL-USDC --venue marginfi # scaffold a Composition
|
|
9
|
+
lien simulate --pool SOL-USDC --steps 240 # backtest against pool history
|
|
10
|
+
lien deploy --cluster devnet # print the on-chain install plan (does NOT broadcast)
|
|
5
11
|
```
|
|
6
12
|
|
|
7
|
-
|
|
13
|
+
The actual broadcast is `lien deploy --cluster <cluster> --execute --keypair <path>`. The `--execute` flag is explicit by design — a fat-fingered `lien deploy` should not write to mainnet.
|
|
8
14
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
lien
|
|
14
|
-
lien
|
|
15
|
-
lien
|
|
16
|
-
|
|
15
|
+
## Six commands
|
|
16
|
+
|
|
17
|
+
| command | what it does |
|
|
18
|
+
|---|---|
|
|
19
|
+
| `lien list` | print the six standard knots with their flags, lifecycle events, params |
|
|
20
|
+
| `lien create <knot> --pool <pair> --venue <lender>` | scaffold a `lien.toml` with the named knot installed |
|
|
21
|
+
| `lien simulate --pool <pair> --steps <n>` | run the Composition against `<n>` steps of pool history; print accept / reject / side-effects |
|
|
22
|
+
| `lien deploy --cluster <cluster>` | print the install plan; `--execute --keypair <path>` to broadcast |
|
|
23
|
+
| `lien action --workflow <name>` | write a GitHub Actions workflow that runs `lien simulate` on every push |
|
|
24
|
+
| `lien publish <knot>` | (coming) wrap `publish_hook` for knot authors |
|
|
25
|
+
|
|
26
|
+
## Six standard knots
|
|
27
|
+
|
|
28
|
+
| knot | code id | fires on |
|
|
29
|
+
|---|---|---|
|
|
30
|
+
| the slip knot | `DynamicLTV` | beforeBorrow / afterDeposit |
|
|
31
|
+
| the timer hitch | `TimeTriggerLiq` | beforeLiquidate |
|
|
32
|
+
| the lock knot | `WhitelistBorrow` | beforeBorrow |
|
|
33
|
+
| the double bowline | `AntiMEVLiq` | beforeLiquidate |
|
|
34
|
+
| the double helix | `AutoHedge` | afterBorrow / afterDeposit |
|
|
35
|
+
| the rolling hitch | `ReputationRate` | beforeBorrow |
|
|
36
|
+
|
|
37
|
+
Each is a real implementation with passing tests on the Rust side. The CLI prints them out so you don't have to open the browser.
|
|
38
|
+
|
|
39
|
+
## Lender targets
|
|
40
|
+
|
|
41
|
+
Marginfi v2 · Kamino Lend · Save (formerly Solend). Adapters ship from source for the moment — `pnpm -r build` from the monorepo covers them — npm publish for adapter packages tracks the next stable release.
|
|
42
|
+
|
|
43
|
+
## Links
|
|
17
44
|
|
|
18
|
-
|
|
45
|
+
- Site: https://liens.fi
|
|
46
|
+
- SDK: `npm i @liens/sdk`
|
|
47
|
+
- Source: https://github.com/liens-fi/lien
|
|
48
|
+
- License: Apache-2.0
|
package/dist/bin.js
CHANGED
|
@@ -5,16 +5,18 @@ import { simulateCommand } from "./commands/simulate.js";
|
|
|
5
5
|
import { deployCommand } from "./commands/deploy.js";
|
|
6
6
|
import { listCommand } from "./commands/list.js";
|
|
7
7
|
import { actionCommand } from "./commands/action.js";
|
|
8
|
+
import { receiptsCommand } from "./commands/receipts.js";
|
|
8
9
|
const program = new Command();
|
|
9
10
|
program
|
|
10
11
|
.name("lien")
|
|
11
12
|
.description("Lien — tie your loans. Create, simulate, and deploy Solana lending hooks.")
|
|
12
|
-
.version("0.1.
|
|
13
|
+
.version("0.1.2");
|
|
13
14
|
program.addCommand(createCommand());
|
|
14
15
|
program.addCommand(simulateCommand());
|
|
15
16
|
program.addCommand(deployCommand());
|
|
16
17
|
program.addCommand(listCommand());
|
|
17
18
|
program.addCommand(actionCommand());
|
|
19
|
+
program.addCommand(receiptsCommand());
|
|
18
20
|
program.parseAsync(process.argv).catch((err) => {
|
|
19
21
|
console.error(err instanceof Error ? err.message : err);
|
|
20
22
|
process.exitCode = 1;
|
package/dist/bin.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bin.js","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"bin.js","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAEzD,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAC9B,OAAO;KACJ,IAAI,CAAC,MAAM,CAAC;KACZ,WAAW,CACV,2EAA2E,CAC5E;KACA,OAAO,CAAC,OAAO,CAAC,CAAC;AAEpB,OAAO,CAAC,UAAU,CAAC,aAAa,EAAE,CAAC,CAAC;AACpC,OAAO,CAAC,UAAU,CAAC,eAAe,EAAE,CAAC,CAAC;AACtC,OAAO,CAAC,UAAU,CAAC,aAAa,EAAE,CAAC,CAAC;AACpC,OAAO,CAAC,UAAU,CAAC,WAAW,EAAE,CAAC,CAAC;AAClC,OAAO,CAAC,UAAU,CAAC,aAAa,EAAE,CAAC,CAAC;AACpC,OAAO,CAAC,UAAU,CAAC,eAAe,EAAE,CAAC,CAAC;AAEtC,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IAC7C,OAAO,CAAC,KAAK,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IACxD,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;AACvB,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"receipts.d.ts","sourceRoot":"","sources":["../../src/commands/receipts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA8DpC,wBAAgB,eAAe,IAAI,OAAO,CA2EzC"}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import kleur from "kleur";
|
|
3
|
+
import { Connection, PublicKey } from "@solana/web3.js";
|
|
4
|
+
const LIEN_EXECUTOR_ID = "5yNMqcyZsGQJk4xvw4jjvoRBSnGs8mgramEa3HQe5faD";
|
|
5
|
+
const EVENT_KIND_LABEL = {
|
|
6
|
+
0: "beforeDeposit",
|
|
7
|
+
1: "afterDeposit",
|
|
8
|
+
2: "beforeBorrow",
|
|
9
|
+
3: "afterBorrow",
|
|
10
|
+
4: "beforeRepay",
|
|
11
|
+
5: "afterRepay",
|
|
12
|
+
6: "beforeLiquidate",
|
|
13
|
+
7: "afterLiquidate",
|
|
14
|
+
};
|
|
15
|
+
function parseExecutorLogs(logs) {
|
|
16
|
+
// The executor emits a CompositionExecuted event via Anchor's `emit!`. Anchor
|
|
17
|
+
// prints "Program data: <base64>" lines for these. We don't decode the full
|
|
18
|
+
// borsh payload here — that's what @liens/sdk does. The CLI surface is a
|
|
19
|
+
// human-readable summary, so we look for the program log markers and pull
|
|
20
|
+
// the obvious bytes (event kind + counts) from the borsh prefix.
|
|
21
|
+
const out = {};
|
|
22
|
+
for (const line of logs) {
|
|
23
|
+
if (!line.includes("Program data:"))
|
|
24
|
+
continue;
|
|
25
|
+
const b64 = line.split("Program data:")[1]?.trim();
|
|
26
|
+
if (!b64)
|
|
27
|
+
continue;
|
|
28
|
+
let buf;
|
|
29
|
+
try {
|
|
30
|
+
buf = Buffer.from(b64, "base64");
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
// Anchor event layout: 8-byte discriminator + struct fields.
|
|
36
|
+
// CompositionExecuted: composition(32) + pool(32) + event_kind(u8) +
|
|
37
|
+
// position_owner(32) + adapter(u8) + hook_count_eligible(u8) +
|
|
38
|
+
// hook_count_skipped(u8) + timestamp(i64)
|
|
39
|
+
if (buf.length < 8 + 32 + 32 + 1 + 32 + 1 + 1 + 1)
|
|
40
|
+
continue;
|
|
41
|
+
out.composition = new PublicKey(buf.subarray(8, 40)).toBase58();
|
|
42
|
+
out.pool = new PublicKey(buf.subarray(40, 72)).toBase58();
|
|
43
|
+
out.event_kind = buf[72];
|
|
44
|
+
// position_owner = bytes 73..105
|
|
45
|
+
// adapter = byte 105
|
|
46
|
+
out.hook_count_eligible = buf[106];
|
|
47
|
+
out.hook_count_skipped = buf[107];
|
|
48
|
+
break;
|
|
49
|
+
}
|
|
50
|
+
return out;
|
|
51
|
+
}
|
|
52
|
+
export function receiptsCommand() {
|
|
53
|
+
return new Command("receipts")
|
|
54
|
+
.description("Pull a HookRan receipt off the chain and pretty-print it. Single signature for now; per-pool window coming next.")
|
|
55
|
+
.option("--signature <sig>", "Transaction signature to inspect")
|
|
56
|
+
.option("--rpc <url>", "RPC endpoint", "https://api.mainnet-beta.solana.com")
|
|
57
|
+
.action(async (opts) => {
|
|
58
|
+
if (!opts.signature) {
|
|
59
|
+
console.error(kleur.red("error:") +
|
|
60
|
+
" --signature is required. example: lien receipts --signature 3AWddY...");
|
|
61
|
+
process.exitCode = 1;
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
const conn = new Connection(opts.rpc, "confirmed");
|
|
65
|
+
const tx = await conn.getTransaction(opts.signature, {
|
|
66
|
+
maxSupportedTransactionVersion: 0,
|
|
67
|
+
});
|
|
68
|
+
if (!tx) {
|
|
69
|
+
console.error(kleur.red("error:") +
|
|
70
|
+
` no transaction found at ${opts.signature} on ${opts.rpc}`);
|
|
71
|
+
process.exitCode = 1;
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
const logs = tx.meta?.logMessages ?? [];
|
|
75
|
+
const parsed = parseExecutorLogs(logs);
|
|
76
|
+
const receipt = {
|
|
77
|
+
signature: opts.signature,
|
|
78
|
+
slot: tx.slot,
|
|
79
|
+
blockTime: tx.blockTime ?? null,
|
|
80
|
+
...parsed,
|
|
81
|
+
};
|
|
82
|
+
console.log(kleur.bold().yellow("lien receipts"));
|
|
83
|
+
console.log(` signature ${receipt.signature}`);
|
|
84
|
+
console.log(` slot ${receipt.slot}`);
|
|
85
|
+
console.log(` block time ${receipt.blockTime
|
|
86
|
+
? new Date(receipt.blockTime * 1000).toISOString()
|
|
87
|
+
: "(unknown)"}`);
|
|
88
|
+
console.log(` executor ${LIEN_EXECUTOR_ID}`);
|
|
89
|
+
if (receipt.event_kind !== undefined) {
|
|
90
|
+
console.log(` event ${EVENT_KIND_LABEL[receipt.event_kind] ?? "?"} (kind=${receipt.event_kind})`);
|
|
91
|
+
console.log(` pool ${receipt.pool ?? "?"}`);
|
|
92
|
+
console.log(` composition ${receipt.composition ?? "?"}`);
|
|
93
|
+
console.log(` hooks eligible ${receipt.hook_count_eligible ?? "?"}`);
|
|
94
|
+
console.log(` hooks skipped ${receipt.hook_count_skipped ?? "?"}`);
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
console.log(kleur.dim(" (no CompositionExecuted event in this transaction — was the signature an executor call?)"));
|
|
98
|
+
}
|
|
99
|
+
// Raw logs for forensics
|
|
100
|
+
console.log("");
|
|
101
|
+
console.log(kleur.dim(" raw program logs:"));
|
|
102
|
+
for (const line of logs) {
|
|
103
|
+
if (line.startsWith("Program log:") || line.includes("Program data:")) {
|
|
104
|
+
console.log(kleur.dim(` ${line}`));
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=receipts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"receipts.js","sourceRoot":"","sources":["../../src/commands/receipts.ts"],"names":[],"mappings":"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"}
|