import { hexToBigInt, type Hex } from "viem";
import { privateKeyToAccount } from "viem/accounts";
const BASE_URL = "https://api-swap.utexo.com/affiliate";
const API_KEY = process.env.UTEXO_SWAP_API_KEY!;
const account = privateKeyToAccount(process.env.PRIVATE_KEY as Hex);
const destinationAddress = process.env.DESTINATION_ADDRESS!;
async function api<T>(path: string, body?: unknown): Promise<T> {
const response = await fetch(`${BASE_URL}${path}`, {
method: body === undefined ? "GET" : "POST",
headers: { "Content-Type": "application/json", "X-API-Key": API_KEY },
body: body === undefined ? undefined : JSON.stringify(body),
});
const text = await response.text();
if (!response.ok) throw new Error(`${path} failed: ${response.status} ${text}`);
return (text ? JSON.parse(text) : undefined) as T;
}
// 1. Quote: WETH on Ethereum -> USDT on Tron
const quote = await api<any>("/v1/quotes/best", {
source_chain: 1,
source_token: "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2",
dest_chain: 2,
dest_token: "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t",
amount: 0.001,
slippage_bps: "50",
swap_type: "standard",
deposit_type: "escrowed",
retail_user_id: null,
user_meta: null,
affiliate_fees: null,
});
// 2. Intent
const intent = await api<any>("/v1/intents", {
quote_id: quote.id,
user_source_address: account.address,
user_destination_address: destinationAddress,
refund_address: account.address,
user_source_public_key: null,
});
if (intent.approval_mechanism !== "permit2") {
throw new Error(`Expected permit2, got ${intent.approval_mechanism}`);
}
// 3. Build the Permit2 typed data
const params = intent.params_to_sign;
const data = params.additional_data;
const toDecimal = (value?: string) => (value ? hexToBigInt(value as Hex).toString() : undefined);
const witness = {
...data.witness,
...(data.witness.minAmountOut ? { minAmountOut: toDecimal(data.witness.minAmountOut) } : {}),
...(data.witness.maxAmountOut ? { maxAmountOut: toDecimal(data.witness.maxAmountOut) } : {}),
...(data.witness.deadline ? { deadline: toDecimal(data.witness.deadline) } : {}),
};
const signature = await account.signTypedData({
domain: {
name: data.domain.name,
chainId: data.domain.chainId,
verifyingContract: data.domain.verifyingContract,
},
types: data.types,
primaryType: "PermitWitnessTransferFrom",
message: {
permitted: { token: quote.source_token, amount: quote.source_amount_lots },
spender: params.escrow_contract_address,
nonce: params.nonce,
deadline: intent.deadline_secs,
witness,
},
});
// 4. Approval
await api(`/v1/intents/${intent.intent_id}/approvals`, {
type: "permit2",
signed_data: signature,
});