import { config } from 'dotenv';
import { privateKeyToAccount } from 'viem/accounts';
import { parseUnits } from 'viem';
import { entryPoint07Address, getUserOperationHash } from 'viem/account-abstraction';
config();
(async () => {
const API_KEY = process.env.ONEBALANCE_API_KEY!;
const privateKey = process.env.PRIVATE_KEY_BASE as `0x${string}`;
const signer = privateKeyToAccount(privateKey);
// Predict account address
const predictResponse = await fetch('<https://be.onebalance.io/api/account/predict-address>', {
method: 'POST',
headers: { 'x-api-key': API_KEY, 'Content-Type': 'application/json' },
body: JSON.stringify({
type: 'kernel-v3.1-ecdsa',
signerAddress: signer.address,
})
});
const { predictedAddress } = await predictResponse.json() as { predictedAddress: string };
// Get quote for USDC → ETH swap
const quoteResponse = await fetch('<https://be.onebalance.io/api/v3/quote>', {
method: 'POST',
headers: { 'x-api-key': API_KEY, 'Content-Type': 'application/json' },
body: JSON.stringify({
from: {
accounts: [{
type: 'kernel-v3.1-ecdsa',
signerAddress: signer.address,
accountAddress: predictedAddress,
}],
asset: { assetId: 'ob:usdc' },
amount: parseUnits('0.5', 6).toString(),
},
to: {
asset: { assetId: 'ob:eth' },
},
})
});
const quote = await quoteResponse.json() as any;
// Sign operation (Kernel v3.1 requires signing UserOp hash)
const operation = quote.originChainsOperations[0];
const userOp = {
sender: operation.userOp.sender,
nonce: BigInt(operation.userOp.nonce),
factory: operation.userOp.factory,
factoryData: operation.userOp.factoryData,
callData: operation.userOp.callData,
callGasLimit: BigInt(operation.userOp.callGasLimit),
verificationGasLimit: BigInt(operation.userOp.verificationGasLimit),
preVerificationGas: BigInt(operation.userOp.preVerificationGas),
maxFeePerGas: BigInt(operation.userOp.maxFeePerGas),
maxPriorityFeePerGas: BigInt(operation.userOp.maxPriorityFeePerGas),
paymaster: operation.userOp.paymaster,
paymasterVerificationGasLimit: operation.userOp.paymasterVerificationGasLimit
? BigInt(operation.userOp.paymasterVerificationGasLimit) : undefined,
paymasterPostOpGasLimit: operation.userOp.paymasterPostOpGasLimit
? BigInt(operation.userOp.paymasterPostOpGasLimit) : undefined,
paymasterData: operation.userOp.paymasterData,
signature: operation.userOp.signature,
};
const userOpHash = getUserOperationHash({
userOperation: userOp,
entryPointAddress: entryPoint07Address,
entryPointVersion: '0.7',
chainId: Number(operation.typedDataToSign.domain.chainId),
});
const signature = await signer.signMessage({ message: { raw: userOpHash } });
quote.originChainsOperations[0].userOp.signature = signature;
// Execute swap
const executeResponse = await fetch('<https://be.onebalance.io/api/v3/quote/execute-quote>', {
method: 'POST',
headers: { 'x-api-key': API_KEY, 'Content-Type': 'application/json' },
body: JSON.stringify(quote)
});
const result = await executeResponse.json() as any;
})();