@biconomy/abstractjs
Version:
SDK for Biconomy integration with support for account abstraction, smart accounts, ERC-4337.
143 lines • 6.07 kB
JavaScript
import { concatHex, isAddress } from "viem";
import { InputParamFetcherType, prepareComposableParams } from "../../../modules/utils/composabilityCalls.js";
import { getFunctionContextFromAbi } from "../../../modules/utils/runtimeAbiEncoding.js";
export const buildComposableCall = async (baseParams, parameters, efficientMode) => {
const { account } = baseParams;
const { to, gasLimit, value, functionName, args, abi, chainId } = parameters;
if (!functionName || !args) {
throw new Error("Invalid params for composable call");
}
if (!abi) {
throw new Error("Invalid ABI");
}
if (!isAddress(to)) {
throw new Error("Invalid target contract address");
}
const smartAccountAddress = account.addressOn(chainId, true);
if (!isAddress(smartAccountAddress)) {
throw new Error("Invalid smart account address");
}
if (args.length <= 0) {
throw new Error("Composable call is not required for a instruction which has zero args");
}
const functionContext = getFunctionContextFromAbi(functionName, abi);
if (functionContext?.inputs?.length !== args?.length) {
throw new Error(`Invalid arguments for the ${functionName} function`);
}
const composableParams = prepareComposableParams([...functionContext.inputs], args);
const composableCalls = [];
const composableCall = {
to: to,
value: value ?? BigInt(0),
functionSig: functionContext.functionSig,
inputParams: efficientMode
? compressInputParams(composableParams)
: composableParams,
//inputParams: composableParams,
outputParams: [], // In the current scope, output params are not handled. When more composability functions are added, this will change
...(gasLimit ? { gasLimit } : {})
};
composableCalls.push(composableCall);
return composableCalls;
};
/**
* Builds an instruction for composable transaction. This is a generic function which creates the composable instructions
* to execute against composability stack
*
* @param baseParams - Base configuration for the instruction
* @param baseParams.account - The account that will execute the composable transaction
* @param baseParams.currentInstructions - Optional array of existing instructions to append to
* @param parameters - Parameters for generate composable instruction
* @param parameters.to - Address of the target contract address
* @param parameters.functionName - Function signature of the composable transaction call
* @param parameters.args - Function arguments of the composable transaction call
* @param parameters.abi - ABI of the contract where the composable transaction call is being generated from
* @param parameters.chainId - Chain where the composable transaction will be executed
* @param [parameters.gasLimit] - Optional gas limit
* @param [parameters.value] - Optional native token value
*
* @returns Promise resolving to array of instructions
*
* @example
* ```typescript
* const instructions = buildComposable(
* { account: myMultichainAccount },
* {
* to: targetContractAddress,
* functionName: 'exactInputSingle',
* args: [
* {
* tokenIn: inToken.addressOn(baseSepolia.id),
* tokenOut: outToken.addressOn(baseSepolia.id),
* fee: 3000,
* recipient: recipient,
* deadline: BigInt(Math.floor(Date.now() / 1000) + 900),
* amountIn: runtimeERC20BalanceOf({ targetAddress: recipient, tokenAddress: testnetMcUSDC.addressOn(baseSepolia.id), constraints: [] }),
* amountOutMinimum: BigInt(1),
* sqrtPriceLimitX96: BigInt(0),
* },
* ]
* chainId: baseSepolia.id,
* abi: UniswapSwapRouterAbi
* }
* )
* ```
*/
export const buildComposableUtil = async (baseParams, parameters, efficientMode = true) => {
const { currentInstructions = [] } = baseParams;
const calls = await buildComposableCall(baseParams, parameters, efficientMode);
return [
...currentInstructions,
{
calls: calls,
chainId: parameters.chainId,
isComposable: true
}
];
};
export default buildComposableUtil;
/**
* Compresses the input params by merging the input params with InputParamFetcherType.RAW_BYTES
* and no constraints together
* It does this by creating a new InputParam with InputParamFetcherType.RAW_BYTES and no constraints
* and paramData as the concat of paramData's
* It allows for less input params in the composable call => less iterations in the composable smart contract
* => less gas used
*/
const compressInputParams = (inputParams) => {
const compressedParams = [];
let currentParam = {
fetcherType: InputParamFetcherType.RAW_BYTES,
constraints: [],
paramData: ""
};
for (const param of inputParams) {
// Static call or constraint based params are left as is
if (param.fetcherType === InputParamFetcherType.STATIC_CALL ||
param.constraints.length > 0) {
// If there is a current param, push it to the compressed params
// and reset the current param
if (currentParam.paramData.length > 0) {
compressedParams.push(currentParam);
currentParam = {
fetcherType: InputParamFetcherType.RAW_BYTES,
constraints: [],
paramData: ""
};
}
compressedParams.push(param);
continue;
}
// If the current param is a raw bytes param with no constraints, merge it with the current param
currentParam.paramData = concatHex([
currentParam.paramData,
param.paramData
]);
}
// If there is a non-empty current param, push it to the compressed params
if (currentParam.paramData.length > 0) {
compressedParams.push(currentParam);
}
return compressedParams;
};
//# sourceMappingURL=buildComposable.js.map