Skip to content

Commit 6ff71ed

Browse files
committed
feat: add runtime abi encoding and fix ts config
1 parent 514b149 commit 6ff71ed

9 files changed

Lines changed: 941 additions & 5 deletions

File tree

src/core/contract/types.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
import type { Abi } from 'abitype';
2-
import type { Address, ContractFunctionArgs, ContractFunctionName, ContractFunctionReturnType } from 'viem';
2+
import type {
3+
Address,
4+
ContractFunctionArgs,
5+
ContractFunctionName,
6+
ContractFunctionReturnType,
7+
} from 'viem';
38

49
export interface ContractInstance<TAbi extends Abi | readonly unknown[]> {
510
readonly address: Address;

src/core/encoding/encoding.ts

Lines changed: 222 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,222 @@
1-
// Runtime composable encoding
1+
import {
2+
encodeAbiParameters,
3+
encodeFunctionData,
4+
encodePacked,
5+
erc20Abi,
6+
isAddress,
7+
isHex,
8+
zeroAddress,
9+
type Hex,
10+
} from 'viem';
11+
import type { AnyData } from '../types';
12+
import {
13+
ConstraintType,
14+
InputParamFetcherType,
15+
OutputParamFetcherType,
16+
type Constraint,
17+
type ConstraintField,
18+
type InputParam,
19+
type OutputParam,
20+
type RuntimeBalanceOfParams,
21+
type runtimeERC20AllowanceOfParams,
22+
type RuntimeNativeBalanceOfParams,
23+
type RuntimeParamViaCustomStaticCallParams,
24+
type RuntimeValue,
25+
} from './types';
26+
import { toBytes32 } from './utils';
27+
28+
export const prepareInputParam = (
29+
fetcherType: InputParamFetcherType,
30+
paramData: string,
31+
constraints: Constraint[] = [],
32+
): InputParam => {
33+
return { fetcherType, paramData, constraints };
34+
};
35+
36+
export const prepareOutputParam = (
37+
fetcherType: OutputParamFetcherType,
38+
paramData: string,
39+
): OutputParam => {
40+
return { fetcherType, paramData };
41+
};
42+
43+
export const prepareConstraint = (
44+
constraintType: ConstraintType,
45+
referenceData: string,
46+
): Constraint => {
47+
return { constraintType, referenceData };
48+
};
49+
50+
// type any is being implicitly used. The appropriate value validation happens in the runtime function
51+
export const greaterThanOrEqualTo = (value: AnyData): ConstraintField => {
52+
return { type: ConstraintType.GTE, value };
53+
};
54+
55+
// type any is being implicitly used. The appropriate value validation happens in the runtime function
56+
export const lessThanOrEqualTo = (value: AnyData): ConstraintField => {
57+
return { type: ConstraintType.LTE, value };
58+
};
59+
60+
// type any is being implicitly used. The appropriate value validation happens in the runtime function
61+
export const equalTo = (value: AnyData): ConstraintField => {
62+
return { type: ConstraintType.EQ, value };
63+
};
64+
65+
/**
66+
* Validates and processes constraints for runtime functions
67+
* @param constraints - Array of constraint fields to validate and process
68+
* @returns Array of processed constraints ready for use
69+
*/
70+
export const validateAndProcessConstraints = (constraints: ConstraintField[]): Constraint[] => {
71+
const constraintsToAdd: Constraint[] = [];
72+
73+
if (constraints.length > 0) {
74+
for (const constraint of constraints) {
75+
// Constraint type IN is ignored for runtime functions
76+
// This is mostly a number/unit/int, so it makes sense to only have EQ, GTE, LTE
77+
if (!Object.values(ConstraintType).slice(0, 3).includes(constraint.type)) {
78+
throw new Error('Invalid constraint type');
79+
}
80+
81+
// Handle value validation in a appropriate to runtime function
82+
if (
83+
typeof constraint.value !== 'bigint' &&
84+
typeof constraint.value !== 'boolean' &&
85+
!isHex(constraint.value) &&
86+
!isAddress(constraint.value)
87+
) {
88+
throw new Error('Invalid constraint value');
89+
}
90+
91+
if (typeof constraint.value === 'bigint' && constraint.value < BigInt(0)) {
92+
throw new Error('Invalid constraint value');
93+
}
94+
95+
const valueHex = toBytes32(constraint.value);
96+
const encodedConstraintValue = encodeAbiParameters([{ type: 'bytes32' }], [valueHex as Hex]);
97+
98+
constraintsToAdd.push(prepareConstraint(constraint.type, encodedConstraintValue));
99+
}
100+
}
101+
102+
return constraintsToAdd;
103+
};
104+
105+
export const runtimeParamViaCustomStaticCall = ({
106+
targetContractAddress,
107+
functionAbi,
108+
functionName,
109+
args,
110+
constraints = [],
111+
}: RuntimeParamViaCustomStaticCallParams): RuntimeValue => {
112+
const encodedParam = encodeAbiParameters(
113+
[{ type: 'address' }, { type: 'bytes' }],
114+
[
115+
targetContractAddress,
116+
encodeFunctionData({
117+
abi: functionAbi,
118+
functionName: functionName,
119+
args,
120+
}),
121+
],
122+
);
123+
124+
const constraintsToAdd = validateAndProcessConstraints(constraints);
125+
126+
return {
127+
isRuntime: true,
128+
inputParams: [
129+
prepareInputParam(InputParamFetcherType.STATIC_CALL, encodedParam, constraintsToAdd),
130+
],
131+
outputParams: [],
132+
};
133+
};
134+
135+
/**
136+
* Returns the runtime value for the ERC20 allowance of the owner for the spender
137+
* @param owner - The owner of the tokens
138+
* @param spender - The spender of the tokens
139+
* @param tokenAddress - The address of the ERC20 token
140+
* @returns The runtime value for the ERC20 allowance of the owner for the spender
141+
*/
142+
export const runtimeERC20AllowanceOf = ({
143+
owner,
144+
spender,
145+
tokenAddress,
146+
constraints = [],
147+
}: runtimeERC20AllowanceOfParams): RuntimeValue => {
148+
const encodedParam = encodeAbiParameters(
149+
[{ type: 'address' }, { type: 'bytes' }],
150+
[
151+
tokenAddress,
152+
encodeFunctionData({
153+
abi: erc20Abi,
154+
functionName: 'allowance',
155+
args: [owner, spender],
156+
}),
157+
],
158+
);
159+
160+
const constraintsToAdd = validateAndProcessConstraints(constraints);
161+
162+
return {
163+
isRuntime: true,
164+
inputParams: [
165+
prepareInputParam(InputParamFetcherType.STATIC_CALL, encodedParam, constraintsToAdd),
166+
],
167+
outputParams: [],
168+
};
169+
};
170+
171+
/**
172+
* Returns the runtime value for the native balance of the target address
173+
* Utilizes the BALANCE fetcherType
174+
* @param targetAddress - The address of the target account
175+
* @returns The runtime value for the native balance of the target address
176+
*/
177+
export const runtimeNativeBalanceOf = ({
178+
targetAddress,
179+
constraints = [],
180+
}: RuntimeNativeBalanceOfParams): RuntimeValue => {
181+
return getBalanceOf({
182+
targetAddress,
183+
tokenAddress: zeroAddress,
184+
constraints,
185+
});
186+
};
187+
188+
/**
189+
* Returns the runtime value for the ERC20 balance of the target address
190+
* @param targetAddress - The address of the target account
191+
* @param tokenAddress - The address of the ERC20 token
192+
* @returns The runtime value for the ERC20 balance of the target address
193+
*/
194+
export const runtimeERC20BalanceOf = ({
195+
targetAddress,
196+
tokenAddress,
197+
constraints = [],
198+
}: RuntimeBalanceOfParams): RuntimeValue => {
199+
return getBalanceOf({
200+
targetAddress,
201+
tokenAddress,
202+
constraints,
203+
});
204+
};
205+
206+
const getBalanceOf = ({
207+
targetAddress,
208+
tokenAddress,
209+
constraints = [],
210+
}: RuntimeBalanceOfParams): RuntimeValue => {
211+
const constraintsToAdd = validateAndProcessConstraints(constraints);
212+
213+
const encodedInputParamData = encodePacked(['address', 'address'], [tokenAddress, targetAddress]);
214+
215+
return {
216+
isRuntime: true,
217+
inputParams: [
218+
prepareInputParam(InputParamFetcherType.BALANCE, encodedInputParamData, constraintsToAdd),
219+
],
220+
outputParams: [],
221+
};
222+
};

0 commit comments

Comments
 (0)