Skip to content

Commit 15e7a65

Browse files
authored
Merge pull request #10 from oralang/abi
basic abi and cmds to see it
2 parents a6f7ba5 + 70be4fb commit 15e7a65

File tree

3 files changed

+488
-31
lines changed

3 files changed

+488
-31
lines changed

src/abi.zig

Lines changed: 273 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,273 @@
1+
const std = @import("std");
2+
const lib = @import("root.zig");
3+
4+
/// ABI Type - Ethereum ABI types
5+
pub const AbiType = union(enum) {
6+
uint: u16, // uint8, uint16, ..., uint256
7+
int: u16, // int8, int16, ..., int256
8+
address,
9+
bool,
10+
bytes,
11+
string,
12+
array: struct {
13+
element_type: *AbiType,
14+
size: ?usize, // null for dynamic arrays
15+
},
16+
17+
pub fn format(self: AbiType, allocator: std.mem.Allocator) ![]const u8 {
18+
return switch (self) {
19+
.uint => |bits| try std.fmt.allocPrint(allocator, "uint{d}", .{bits}),
20+
.int => |bits| try std.fmt.allocPrint(allocator, "int{d}", .{bits}),
21+
.address => try allocator.dupe(u8, "address"),
22+
.bool => try allocator.dupe(u8, "bool"),
23+
.bytes => try allocator.dupe(u8, "bytes"),
24+
.string => try allocator.dupe(u8, "string"),
25+
.array => |arr| blk: {
26+
const elem_str = try arr.element_type.format(allocator);
27+
defer allocator.free(elem_str);
28+
if (arr.size) |size| {
29+
break :blk try std.fmt.allocPrint(allocator, "{s}[{d}]", .{ elem_str, size });
30+
} else {
31+
break :blk try std.fmt.allocPrint(allocator, "{s}[]", .{elem_str});
32+
}
33+
},
34+
};
35+
}
36+
};
37+
38+
/// ABI Parameter
39+
pub const AbiParameter = struct {
40+
name: []const u8,
41+
type: AbiType,
42+
allocator: std.mem.Allocator,
43+
44+
pub fn deinit(self: *AbiParameter) void {
45+
_ = self;
46+
// Types are managed by the generator
47+
}
48+
};
49+
50+
/// ABI Function
51+
pub const AbiFunction = struct {
52+
name: []const u8,
53+
inputs: []AbiParameter,
54+
outputs: []AbiParameter,
55+
state_mutability: []const u8, // "pure", "view", "nonpayable", "payable"
56+
function_type: []const u8, // "function", "constructor", "fallback", "receive"
57+
allocator: std.mem.Allocator,
58+
59+
pub fn deinit(self: *AbiFunction) void {
60+
for (self.inputs) |*input| {
61+
input.deinit();
62+
}
63+
self.allocator.free(self.inputs);
64+
for (self.outputs) |*output| {
65+
output.deinit();
66+
}
67+
self.allocator.free(self.outputs);
68+
}
69+
};
70+
71+
/// Contract ABI
72+
pub const ContractAbi = struct {
73+
functions: []AbiFunction,
74+
allocator: std.mem.Allocator,
75+
76+
pub fn deinit(self: *ContractAbi) void {
77+
for (self.functions) |*func| {
78+
func.deinit();
79+
}
80+
self.allocator.free(self.functions);
81+
}
82+
83+
/// Generate JSON representation of the ABI
84+
pub fn toJson(self: *const ContractAbi, allocator: std.mem.Allocator) ![]const u8 {
85+
var buffer = try std.ArrayList(u8).initCapacity(allocator, 1024);
86+
defer buffer.deinit(allocator);
87+
const writer = buffer.writer(allocator);
88+
89+
try writer.writeAll("[\n");
90+
91+
for (self.functions, 0..) |func, i| {
92+
try writer.writeAll(" {\n");
93+
try writer.print(" \"type\": \"{s}\",\n", .{func.function_type});
94+
try writer.print(" \"name\": \"{s}\",\n", .{func.name});
95+
96+
// Inputs
97+
try writer.writeAll(" \"inputs\": [\n");
98+
for (func.inputs, 0..) |input, j| {
99+
const type_str = try input.type.format(allocator);
100+
defer allocator.free(type_str);
101+
try writer.writeAll(" {\n");
102+
try writer.print(" \"name\": \"{s}\",\n", .{input.name});
103+
try writer.print(" \"type\": \"{s}\"\n", .{type_str});
104+
if (j < func.inputs.len - 1) {
105+
try writer.writeAll(" },\n");
106+
} else {
107+
try writer.writeAll(" }\n");
108+
}
109+
}
110+
try writer.writeAll(" ],\n");
111+
112+
// Outputs
113+
try writer.writeAll(" \"outputs\": [\n");
114+
for (func.outputs, 0..) |output, j| {
115+
const type_str = try output.type.format(allocator);
116+
defer allocator.free(type_str);
117+
try writer.writeAll(" {\n");
118+
try writer.print(" \"type\": \"{s}\"\n", .{type_str});
119+
if (j < func.outputs.len - 1) {
120+
try writer.writeAll(" },\n");
121+
} else {
122+
try writer.writeAll(" }\n");
123+
}
124+
}
125+
try writer.writeAll(" ],\n");
126+
127+
try writer.print(" \"stateMutability\": \"{s}\"\n", .{func.state_mutability});
128+
129+
if (i < self.functions.len - 1) {
130+
try writer.writeAll(" },\n");
131+
} else {
132+
try writer.writeAll(" }\n");
133+
}
134+
}
135+
136+
try writer.writeAll("]\n");
137+
return buffer.toOwnedSlice(allocator);
138+
}
139+
};
140+
141+
/// ABI Generator - extracts ABI from AST
142+
pub const AbiGenerator = struct {
143+
allocator: std.mem.Allocator,
144+
functions: std.ArrayList(AbiFunction),
145+
146+
pub fn init(allocator: std.mem.Allocator) !AbiGenerator {
147+
const functions = std.ArrayList(AbiFunction).initCapacity(allocator, 0) catch |err| {
148+
std.debug.print("Failed to initialize functions list: {}\n", .{err});
149+
return err;
150+
};
151+
return .{
152+
.allocator = allocator,
153+
.functions = functions,
154+
};
155+
}
156+
157+
pub fn deinit(self: *AbiGenerator) void {
158+
for (self.functions.items) |*func| {
159+
func.deinit();
160+
}
161+
self.functions.deinit(self.allocator);
162+
}
163+
164+
/// Generate ABI from AST nodes
165+
pub fn generate(self: *AbiGenerator, ast_nodes: []lib.AstNode) !ContractAbi {
166+
for (ast_nodes) |node| {
167+
try self.processNode(node);
168+
}
169+
170+
return ContractAbi{
171+
.functions = try self.functions.toOwnedSlice(self.allocator),
172+
.allocator = self.allocator,
173+
};
174+
}
175+
176+
fn processNode(self: *AbiGenerator, node: lib.AstNode) !void {
177+
switch (node) {
178+
.Contract => |contract| {
179+
for (contract.body) |body_node| {
180+
try self.processBodyNode(body_node);
181+
}
182+
},
183+
else => {},
184+
}
185+
}
186+
187+
fn processBodyNode(self: *AbiGenerator, node: lib.AstNode) !void {
188+
switch (node) {
189+
.Function => |func| {
190+
// Only include public functions in ABI
191+
if (func.visibility == .Public) {
192+
try self.addFunction(func);
193+
}
194+
},
195+
else => {},
196+
}
197+
}
198+
199+
fn addFunction(self: *AbiGenerator, func: lib.FunctionNode) !void {
200+
// Convert parameters to ABI inputs
201+
var inputs = std.ArrayList(AbiParameter).initCapacity(self.allocator, func.parameters.len) catch |err| {
202+
std.debug.print("Failed to allocate inputs array: {}\n", .{err});
203+
return err;
204+
};
205+
defer inputs.deinit(self.allocator);
206+
207+
for (func.parameters) |param| {
208+
const abi_type = try self.convertType(param.type_info);
209+
const param_abi = AbiParameter{
210+
.name = param.name,
211+
.type = abi_type,
212+
.allocator = self.allocator,
213+
};
214+
try inputs.append(self.allocator, param_abi);
215+
}
216+
217+
// Convert return type to ABI outputs
218+
var outputs = std.ArrayList(AbiParameter).initCapacity(self.allocator, 1) catch |err| {
219+
std.debug.print("Failed to allocate outputs array: {}\n", .{err});
220+
return err;
221+
};
222+
defer outputs.deinit(self.allocator);
223+
224+
if (func.return_type_info) |ret_type| {
225+
const abi_type = try self.convertType(ret_type);
226+
const output_abi = AbiParameter{
227+
.name = "",
228+
.type = abi_type,
229+
.allocator = self.allocator,
230+
};
231+
try outputs.append(self.allocator, output_abi);
232+
}
233+
234+
// Determine state mutability (simplified for now)
235+
const state_mutability = "nonpayable"; // TODO: Analyze function body for state changes
236+
237+
const func_abi = AbiFunction{
238+
.name = func.name,
239+
.inputs = try inputs.toOwnedSlice(self.allocator),
240+
.outputs = try outputs.toOwnedSlice(self.allocator),
241+
.state_mutability = state_mutability,
242+
.function_type = "function",
243+
.allocator = self.allocator,
244+
};
245+
try self.functions.append(self.allocator, func_abi);
246+
}
247+
248+
fn convertType(self: *AbiGenerator, type_info: lib.ast.type_info.TypeInfo) !AbiType {
249+
_ = self;
250+
if (type_info.ora_type) |ora_type| {
251+
return switch (ora_type) {
252+
.u8 => AbiType{ .uint = 8 },
253+
.u16 => AbiType{ .uint = 16 },
254+
.u32 => AbiType{ .uint = 32 },
255+
.u64 => AbiType{ .uint = 64 },
256+
.u128 => AbiType{ .uint = 128 },
257+
.u256 => AbiType{ .uint = 256 },
258+
.i8 => AbiType{ .int = 8 },
259+
.i16 => AbiType{ .int = 16 },
260+
.i32 => AbiType{ .int = 32 },
261+
.i64 => AbiType{ .int = 64 },
262+
.i128 => AbiType{ .int = 128 },
263+
.i256 => AbiType{ .int = 256 },
264+
.bool => AbiType.bool,
265+
.address => AbiType.address,
266+
.string => AbiType.string,
267+
.bytes => AbiType.bytes,
268+
else => AbiType{ .uint = 256 }, // Default to uint256 for unknown types
269+
};
270+
}
271+
return AbiType{ .uint = 256 }; // Default fallback
272+
}
273+
};

0 commit comments

Comments
 (0)