Swap Shorttail → Native token
Last updated
Last updated
E.g: OP → 0.01 ETH on Optimism (Chain 10)
Since this is a L2 (optimism) example, we use packed
form values, as explained here. In this case, we need to call the packedTransmitAndSwap
method from clipper’s smart contract which has the following interface:
function packedTransmitAndSwap(uint256 packedInput, uint256 packedOutput, uint256 packedGoodUntil, bytes32 auxData, bytes32 r, bytes32 vs)
const fetch = require('node-fetch');
import ethers from "ethers";
import packedExchangeAbi from "./packedExchangeAbi.json";
import { hexZeroPad } from 'ethers/lib/utils';
// Get a quote
async function getQuote() {
const quotePayload = {
"output_asset_symbol": "ETH",
"input_asset_symbol": "OP",
"chain_id": 10,
"time_in_seconds": 60,
"output_amount": "10000000000000000"
};
const requestOptions = {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(quotePayload)
};
const response = await fetch('https://api.clipper.exchange/rfq/quote', requestOptions);
const quote = await response.json();
return quote;
}
// Sign the quote
async function signQuote(quoteId) {
const signPayload = {
"quote_id": quoteId,
"destination_address": "0xab83Af831dfb4028EBFd3fFA74A828a4d5DCaAC5"
};
const requestOptions = {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(signPayload)
};
const response = await fetch('https://api.clipper.exchange/rfq/sign', requestOptions);
const signResponse = await response.json();
return signResponse;
}
// Execute transaction
async function executeSwap(signResponse) {
const provider = new ethers.providers.JsonRpcProvider(process.env.RPC_URL);
const clipperPackedContract = new ethers.Contract(
signResponse.clipper_exchange_address,
packedExchangeAbi,
provider
);
const auxData = "0x00000000000000000000000000000000000000000000000000";
const packedInput = packAddressAndAmount(signResponse.input_amount, signResponse.input_asset_address);
const packedOutput = packAddressAndAmount(signResponse.output_amount, signResponse.output_asset_address);
const packedGoodUntil = signResponse.good_until;
const packedData = packAddressAndAmount(auxData, signResponse.destination_address);
const r = byte32(signResponse.signature.r);
const vs = byte32(shortenSignature(signResponse.signature.s, signResponse.signature.v));
const result = await clipperPackedContract.packedTransmitAndSwap(
packedInput, packedOutput, packedGoodUntil, auxData, r, vs
);
}
// In order to calculate the packed values, we can use the following methods
function packAddressAndAmount(amount, address) {
const addressBn = BigInt(address);
const amountBn = BigInt(amount);
return (amountBn << 160n) + addressBn;
}
// Converts the value to 32 bytes and fills the rest with leading zeroes.
function byte32(value) {
return hexZeroPad(value.toString(16), 32);
}
function shortenSignature(s, v) {
const parity = BigInt(v - 27);
const shiftedParity = parity << 255n;
return s + shiftedParity.toString(16);
}
// Main function
async function main() {
// 1. Get a quotey
const quote = await getQuote();
console.log("Quote:", quote);
// 2. Sign a quote
const signResponse = await signQuote(quote.id);
console.log("Sign Response:", signResponse);
// 3. Execute transaction
await executeSwap(signResponse);
console.log("Swap executed successfully.");
}