Swapping Tokens on OP_NET
Here’s how to set up a token swap using OP_NET. This involves approving the tokens and executing a swap between two tokens (e.g., wBTC and another OP_20 token) on the OP_NET metaprotocol using a decentralized exchange like Motoswap.
Step 1: Install Required Packages
Ensure you have the following dependencies installed:
npm install opnet bitcoinjs-lib
# or
yarn add opnet bitcoinjs-lib
Step 2: Set Up the Provider
Initialize the provider to communicate with the OP_NET metaprotocol:
import * as bitcoinjs from "bitcoinjs-lib";
import { JSONRpcProvider } from "opnet";
// Define the OP_NET RPC provider
const rpcUrl = "https://regtest.opnet.org";
const provider = new JSONRpcProvider(rpcUrl);
// Define the Bitcoin network (e.g., regtest, mainnet)
const network = bitcoinjs.networks.regtest;
Step 5: Perform the Swap
import {
IInteractionParameters,
TransactionFactory,
Wallet,
} from "@btc-vision/transaction";
import {
getContract,
IMotoswapRouterContract,
MOTOSWAP_ROUTER_ABI,
} from "opnet";
/**
* Swaps tokens using a decentralized exchange on the OP_NET metaprotocol.
*
* @param {JSONRpcProvider} provider - The JSON RPC provider for interacting with OP_NET.
* @param {Wallet} walletGet - The wallet object with keypairs and addresses.
* @param {bigint} amountIn - The amount of tokens to swap in (in satoshis).
* @param {string} inputAddress - The address of the token to swap from (input token).
* @param {string} outputAddress - The address of the token to swap to (output token).
* @param {bitcoinjs.Network} network - The BitcoinJS network (e.g., regtest, mainnet).
* @param {number} slippagePercentage - The acceptable slippage percentage for the swap.
* @returns The transaction result.
*/
async function swapTokens(
provider: JSONRpcProvider,
walletGet: Wallet,
amountIn: bigint,
inputAddress: string,
outputAddress: string,
network: bitcoinjs.Network,
slippagePercentage: number
) {
try {
// Actual router address on regtest
const routerAddress = "bcrt1q9yd6mk324k0q4krmlxjky0pk65ul6hkf4u35e6";
// Get the swap contract from Motoswap using the router address
const getSwap = getContract<IMotoswapRouterContract>(
routerAddress,
MOTOSWAP_ROUTER_ABI,
provider,
walletGet.p2tr
);
const maxUint256 = BigInt(
"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
);
// Fetch UTXOs for the wallet's addresses
const utxos = await getUTXOs(walletGet.addresses, rpcUrl, maxUint256);
// Approve the input token (wBTC) for the transaction
const approvedData = await approveToken(
walletGet,
inputAddress,
amountIn,
utxos
);
if (!approvedData) {
return {
success: false,
message: "An error occurred while approving the token.",
};
}
// Approve the output token for the swap
const nextUtxos = await approveToken(
walletGet,
outputAddress,
amountIn,
approvedData
);
if (!nextUtxos) {
return {
success: false,
message: "An error occurred while approving the token.",
};
}
// Get the estimated amount out based on the input token amount
const amountOut = await getSwap.getAmountsOut(amountIn, [
inputAddress,
outputAddress,
]);
if ("error" in amountOut) {
return { success: false, message: amountOut.error };
}
// Calculate minimum amount out with slippage tolerance
const amountOutMin = amountOut.decoded[0].toString().split(",")[1];
const amountOutBigInt = BigInt(amountOutMin);
const finalAmountOutWithSlippage =
amountOutBigInt - (amountOutBigInt * BigInt(slippagePercentage)) / 100n;
// Set the transaction deadline (current time + 2 minutes)
const deadline = Math.floor(Date.now() / 1000) + 60 * 2;
// Prepare the calldata for the swap transaction
const contractResult = getSwap.encodeCalldata(
"swapExactTokensForTokensSupportingFeeOnTransferTokens",
[
amountIn,
finalAmountOutWithSlippage,
[inputAddress, outputAddress],
walletGet.p2tr,
BigInt(deadline),
]
);
// Set up transaction parameters for the interaction
const interactionParameters: IInteractionParameters = {
from: walletGet.p2tr,
to: routerAddress,
utxos: nextUtxos,
signer: walletGet.keypair,
network,
feeRate: 100,
priorityFee: 9500n,
calldata: contractResult as Buffer,
};
// Sign and broadcast the transaction
const limitedProvider = new OPNetLimitedProvider(
"https://regtest.opnet.org"
); // Initialize the OP_NET provider
const transactionFactory = new TransactionFactory();
const sendTransact = await transactionFactory.signInteraction(
interactionParameters
);
const firstTransaction = await limitedProvider.broadcastTransaction(
sendTransact[0],
false
);
if (!firstTransaction || !firstTransaction.success) {
return {
success: false,
message: "Could not broadcast first transaction",
};
}
const secondTransaction = await limitedProvider.broadcastTransaction(
sendTransact[1],
false
);
if (!secondTransaction || !secondTransaction.success) {
return {
success: false,
message: "Could not broadcast second transaction",
};
}
return { success: true, txId: secondTransaction.result, amountOutBigInt };
} catch (error) {
console.error("Token swap failed:", error);
return {
success: false,
message: "An error occurred. Please try again.",
};
}
}
// Example usage:
async function main() {
const walletWif = "your-wallet-private-key-in-wif-format"; // Replace with your WIF private key
const walletGet = Wallet.fromWif(walletWif, network); // Import wallet using WIF format
const inputAddress = "bcrt1qamv2ejattjgsc6k3yf3zqrp0wpuyedqgjmwx0v"; // Actual wBTC address on regtest
const outputAddress = "your-token-address"; // Replace with your token's address
const amountIn = 100_000n; // Amount of tokens to swap in satoshis
const slippagePercentage = 2; // Slippage tolerance percentage
const result = await swapTokens(
provider,
walletGet,
amountIn,
inputAddress,
outputAddress,
network,
slippagePercentage
);
if (result.success) {
console.log("Token successfully swapped. Transaction ID:", result.txId);
} else {
console.error("Swap failed:", result.message);
}
}
main();
Last updated