Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions build.zig.zon
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
.fingerprint = 0xfab90a2f402a7502,
.dependencies = .{
.eth = .{
.url = "git+https://github.com/StrobeLabs/eth.zig.git#v0.2.2",
.hash = "eth_zig-0.2.2-efEm-kNdCQBcyYFAZG0FVkmPeN7lJkLLBDaTFp0T7lSD",
.url = "git+https://github.com/StrobeLabs/eth.zig.git#v0.3.0",
.hash = "eth_zig-0.3.0-efEm-i65QgC_yawMMg1E1xrc3E8yTZsAlfhj0wJjPyMe",
},
},
.paths = .{
Expand Down
70 changes: 67 additions & 3 deletions src/abi/perp_manager_abi.zig
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,9 @@ pub const quote_close_position: Function = .{
.{ .name = "unexpectedReason", .abi_type = .bytes },
.{ .name = "pnl", .abi_type = .int256 },
.{ .name = "funding", .abi_type = .int256 },
.{ .name = "netMargin", .abi_type = .uint256 },
.{ .name = "netMargin", .abi_type = .int256 },
.{ .name = "wasLiquidated", .abi_type = .bool },
.{ .name = "notional", .abi_type = .uint256 },
},
};
pub const quote_close_position_selector = keccak.comptimeSelector("quoteClosePosition(uint256)");
Expand Down Expand Up @@ -110,14 +111,13 @@ pub const create_perp: Function = .{
.{ .name = "marginRatios", .abi_type = .address },
.{ .name = "lockupPeriod", .abi_type = .address },
.{ .name = "sqrtPriceImpactLimit", .abi_type = .address },
.{ .name = "startingSqrtPriceX96", .abi_type = .uint160 },
} },
},
.outputs = &.{
.{ .name = "", .abi_type = .bytes32 },
},
};
pub const create_perp_selector = keccak.comptimeSelector("createPerp((address,address,address,address,address,uint160))");
pub const create_perp_selector = keccak.comptimeSelector("createPerp((address,address,address,address,address))");

pub const open_taker_pos: Function = .{
.name = "openTakerPos",
Expand Down Expand Up @@ -317,5 +317,69 @@ pub const position_closed: Event = .{
.inputs = &.{
.{ .name = "perpId", .abi_type = .bytes32, .indexed = true },
.{ .name = "posId", .abi_type = .uint256, .indexed = true },
.{ .name = "exitPerpDelta", .abi_type = .int256 },
.{ .name = "exitUsdDelta", .abi_type = .int256 },
.{ .name = "tickLower", .abi_type = .int24 },
.{ .name = "tickUpper", .abi_type = .int24 },
.{ .name = "netUsdDelta", .abi_type = .int256 },
.{ .name = "funding", .abi_type = .int256 },
.{ .name = "utilizationFee", .abi_type = .uint256 },
.{ .name = "adl", .abi_type = .uint256 },
.{ .name = "liquidationFee", .abi_type = .uint256 },
.{ .name = "netMargin", .abi_type = .int256 },
},
};

pub const module_registered: Event = .{
.name = "ModuleRegistered",
.inputs = &.{
.{ .name = "moduleType", .abi_type = .uint8, .indexed = true },
.{ .name = "module", .abi_type = .address, .indexed = true },
},
};

// -- Module registration functions --

pub const register_module: Function = .{
.name = "registerModule",
.state_mutability = .nonpayable,
.inputs = &.{
.{ .name = "moduleType", .abi_type = .uint8 },
.{ .name = "module", .abi_type = .address },
},
.outputs = &.{},
};
pub const register_module_selector = keccak.comptimeSelector("registerModule(uint8,address)");

pub const is_module_registered: Function = .{
.name = "isModuleRegistered",
.state_mutability = .view,
.inputs = &.{
.{ .name = "moduleType", .abi_type = .uint8 },
.{ .name = "module", .abi_type = .address },
},
.outputs = &.{
.{ .name = "", .abi_type = .bool },
},
};
pub const is_module_registered_selector = keccak.comptimeSelector("isModuleRegistered(uint8,address)");

// -- Quote swap --

pub const quote_swap: Function = .{
.name = "quoteSwap",
.state_mutability = .nonpayable,
.inputs = &.{
.{ .name = "perpId", .abi_type = .bytes32 },
.{ .name = "zeroForOne", .abi_type = .bool },
.{ .name = "isExactIn", .abi_type = .bool },
.{ .name = "amount", .abi_type = .uint256 },
.{ .name = "sqrtPriceLimitX96", .abi_type = .uint160 },
},
.outputs = &.{
.{ .name = "unexpectedReason", .abi_type = .bytes },
.{ .name = "perpDelta", .abi_type = .int256 },
.{ .name = "usdDelta", .abi_type = .int256 },
},
};
pub const quote_swap_selector = keccak.comptimeSelector("quoteSwap(bytes32,bool,bool,uint256,uint160)");
Comment on lines +333 to +385

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Look for wrapper/context usage of the new ABI surface outside the ABI declaration file.
rg -n -C3 'quote_swap|quoteSwap|register_module|registerModule|is_module_registered|isModuleRegistered|module_registered|ModuleRegistered' src -g '!src/abi/perp_manager_abi.zig'

Repository: StrobeLabs/perpcity-zig-sdk

Length of output: 53


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 1. Verify the new ABI symbols exist in the target file
echo "=== Checking for new ABI symbols in perp_manager_abi.zig ==="
rg -n 'quote_swap|register_module|is_module_registered|module_registered' src/abi/perp_manager_abi.zig

# 2. Check how existing aligned functions (createPerp, quoteClosePosition) are used
echo -e "\n=== Checking usage of createPerp and quoteClosePosition outside ABI ==="
rg -n 'create_perp|createPerp|quote_close|quoteClose' src -g '!src/abi/perp_manager_abi.zig' | head -20

# 3. Search entire repo (not just src) for the new symbols
echo -e "\n=== Searching entire repo for new ABI symbols ==="
rg 'quote_swap|quoteSwap|register_module|registerModule|is_module_registered|isModuleRegistered' . -g '!.git' 2>/dev/null || true

Repository: StrobeLabs/perpcity-zig-sdk

Length of output: 1848


These new ABI entries lack wrapper functions and are not exposed at the SDK level.

The registerModule, isModuleRegistered, and quoteSwap symbols are declared in the ABI but have no corresponding wrapper functions in src/perp_manager.zig or src/context.zig. In contrast, existing functions like createPerp (line 44 of perp_manager.zig) and quoteClosePosition (line 232 of context.zig) are wrapped and exposed. These new symbols should follow the same pattern to be consumable from the SDK.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/abi/perp_manager_abi.zig` around lines 333 - 385, The ABI adds
ModuleRegistered event and functions registerModule, isModuleRegistered, and
quoteSwap but no SDK wrappers exist; add wrapper functions mirroring existing
patterns (see createPerp in perp_manager.zig and quoteClosePosition in
context.zig) that call the corresponding ABI selectors
(register_module_selector, is_module_registered_selector, quote_swap_selector),
marshal inputs/outputs to Zig types, emit/consume the ModuleRegistered event as
needed, and expose these wrappers from perp_manager.zig and/or context.zig so
the SDK can call registerModule(moduleType, module),
isModuleRegistered(moduleType, module) -> bool, and quoteSwap(perpId,
zeroForOne, isExactIn, amount, sqrtPriceLimitX96) -> (bytes, int256, int256).

4 changes: 2 additions & 2 deletions src/context.zig
Original file line number Diff line number Diff line change
Expand Up @@ -231,14 +231,14 @@ pub const PerpCityContext = struct {
self.deployments.perp_manager,
perp_manager_abi.quote_close_position_selector,
&.{.{ .uint256 = pos_id }},
&.{ .bytes, .int256, .int256, .uint256, .bool },
&.{ .bytes, .int256, .int256, .int256, .bool, .uint256 },
);
defer contract.freeReturnValues(result, self.allocator);

const live = types.LiveDetails{
.pnl = conversions.scaleFrom6Decimals(@intCast(result[1].int256)),
.funding_payment = conversions.scaleFrom6Decimals(@intCast(result[2].int256)),
.effective_margin = conversions.scaleFrom6Decimals(@intCast(result[3].uint256)),
.effective_margin = conversions.scaleFrom6Decimals(@intCast(result[3].int256)),
.is_liquidatable = result[4].boolean,
};

Expand Down
9 changes: 0 additions & 9 deletions src/perp_manager.zig
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ pub const PerpManagerError = error{
MarginMustBePositive,
LeverageMustBePositive,
InvalidPriceRange,
StartingPriceMustBePositive,
MarginRatioOutOfRange,
ModuleAddressRequired,
TransactionReverted,
Expand All @@ -43,13 +42,6 @@ const POSITION_CLOSED_TOPIC = keccak.comptimeHash("PositionClosed(bytes32,uint25
// ---------------------------------------------------------------------------

pub fn createPerp(ctx: *PerpCityContext, params: types.CreatePerpParams) !types.Bytes32 {
if (params.starting_price <= 0.0) {
return PerpManagerError.StartingPriceMustBePositive;
}

const sqrt_price_x96 = try conversions.priceToSqrtPriceX96(params.starting_price);
const sqrt_price_u160: u160 = @truncate(sqrt_price_x96);

const fees_addr = params.fees orelse ctx.deployments.fees_module orelse types.ZERO_ADDRESS;
const margin_ratios_addr = params.margin_ratios orelse ctx.deployments.margin_ratios_module orelse types.ZERO_ADDRESS;
const lockup_addr = params.lockup_period orelse ctx.deployments.lockup_period_module orelse types.ZERO_ADDRESS;
Expand All @@ -66,7 +58,6 @@ pub fn createPerp(ctx: *PerpCityContext, params: types.CreatePerpParams) !types.
.{ .address = margin_ratios_addr },
.{ .address = lockup_addr },
.{ .address = sqrt_limit_addr },
.{ .uint256 = @as(u256, sqrt_price_u160) },
} }},
);

Expand Down
1 change: 0 additions & 1 deletion src/types.zig
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,6 @@ pub const OpenMakerPositionParams = struct {
};

pub const CreatePerpParams = struct {
starting_price: f64,
beacon: Address,
fees: ?Address = null,
margin_ratios: ?Address = null,
Expand Down