Skip to content

Commit 768c70e

Browse files
authored
feat: hook_pos (#31)
1 parent b2c7c53 commit 768c70e

File tree

3 files changed

+118
-0
lines changed

3 files changed

+118
-0
lines changed

hooks-rs/examples/hook_pos.rs

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#![no_std]
2+
#![no_main]
3+
4+
use hooks_rs::*;
5+
6+
#[no_mangle]
7+
pub extern "C" fn cbak(_: u32) -> i64 {
8+
0
9+
}
10+
11+
#[no_mangle]
12+
pub extern "C" fn hook(_: u32) -> i64 {
13+
max_iter(1);
14+
15+
let hook_pos = hook_pos();
16+
17+
accept(b"", hook_pos);
18+
}

hooks-rs/src/api/hook.rs

+13
Original file line numberDiff line numberDiff line change
@@ -57,3 +57,16 @@ pub fn hook_param<const HOOK_PARAM_LEN: usize>(
5757

5858
init_buffer_mut(func)
5959
}
60+
61+
/// Returns the position in the hook chain the currently executing hook occupies.
62+
/// Returns the position in the chain the currently executing hook occupies. The first position is 0.
63+
///
64+
/// # Example
65+
/// ```
66+
/// // hook_pos is 0 for the first hook in the chain
67+
/// let hook_pos = hook_pos();
68+
/// ```
69+
#[inline(always)]
70+
pub fn hook_pos() -> i64 {
71+
unsafe { c::hook_pos() }
72+
}

hooks-rs/tests/hook_pos.test.ts

+87
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
// xrpl
2+
import { Client, Invoke, Transaction, Wallet } from "@transia/xrpl";
3+
import { TestUtils } from "./setup";
4+
import { HookExecution } from "@transia/xrpl/dist/npm/models/transactions/metadata";
5+
6+
const HOOK_NAME = "hook_pos";
7+
8+
describe("hook_pos.rs", () => {
9+
let client: Client;
10+
let alice: Wallet;
11+
let bob: Wallet;
12+
13+
beforeAll(async () => {
14+
const hook = await TestUtils.buildHook(HOOK_NAME);
15+
client = new Client("wss://xahau-test.net", {});
16+
await client.connect();
17+
client.networkID = await client.getNetworkID();
18+
// Because Faucet only allows one account to be created every 60 seconds,
19+
// we will use the following accounts for testing. Change the secrets when
20+
// running out of funds.
21+
// rLuT6H5sRaANwzphL3vLxD3cM3uDEhjs4J
22+
alice = Wallet.fromSecret(`shEqcVgyFdo5ARiFXhhBLmtg37mgp`);
23+
// rJVeCKt982a2n33SsnQfM2EdurTsGN8Hhz
24+
bob = Wallet.fromSecret(`ssh8Y2q2EeFDTLNS6m5BPhYGExxqb`);
25+
await TestUtils.setHook(client, alice.seed!, hook);
26+
}, 3 * 60_000);
27+
28+
afterAll(async () => {
29+
await client.disconnect();
30+
}, 10_000);
31+
32+
it(
33+
"accepts with the hook pos which is 0",
34+
async () => {
35+
const tx: Invoke & Transaction = {
36+
TransactionType: "Invoke",
37+
Account: bob.classicAddress,
38+
Destination: alice.classicAddress,
39+
};
40+
// Autofilling fee does not work with hooks yet
41+
const { Fee, ...rest } = await client.autofill(tx);
42+
const fee = await TestUtils.getTransactionFee(client, rest);
43+
const txResponse = await TestUtils.submitAndWaitWithRetries(
44+
client,
45+
{
46+
...tx,
47+
Fee: fee,
48+
},
49+
{
50+
wallet: bob,
51+
autofill: true,
52+
},
53+
);
54+
if (!txResponse.result.meta) {
55+
throw new Error("No meta in tx response");
56+
}
57+
if (typeof txResponse.result.meta === "string") {
58+
throw new Error("Meta is string, not object");
59+
}
60+
61+
const { meta } = txResponse.result;
62+
if (!(meta.HookExecutions && meta.HookExecutions.length > 0)) {
63+
throw new Error(`Hook execution data is empty`);
64+
}
65+
66+
if (meta.HookExecutions.length > 1) {
67+
throw new Error(`Hook execution happened more than once`);
68+
}
69+
70+
if (txResponse.result.meta.TransactionResult !== "tesSUCCESS") {
71+
console.error(JSON.stringify(txResponse, null, 2));
72+
73+
throw new Error(`Transaction failed`);
74+
}
75+
76+
// safe type: we checked everything
77+
const [hookExecution] = meta.HookExecutions as [HookExecution];
78+
79+
const { HookReturnString, HookReturnCode } = hookExecution.HookExecution;
80+
81+
expect(HookReturnString).toMatch("");
82+
// HookPos should be 0
83+
expect(Number(HookReturnCode)).toBe(0);
84+
},
85+
3 * 60_000,
86+
);
87+
});

0 commit comments

Comments
 (0)