Swaps
Overview
Swaps represent the simplest intent, the offer to buy/sell at a price. Swaps can be between tokens, NFTs, other swaps, or bonds. Swaps can be fulfilled directly from inventory or by engineering a route via liquidity.
The Fabriq SDK provides the IntentClient class as an API for creation, approval and submission of intents. It also provides abstractions for fetching metadata from the intentpool and on-chain to track the state of intents.
The Swaps section cover:
Issuing Swaps
Checking the Status of a Swap
Querying Swaps in UnEx
Winning Auctions for Swaps
Fulfilling a Swap
Finalizing a Swap
Initializing the Environment
For ease the guides will assume a common environment/setup:
import { IntentClient, intentPoolURL } from 'fabriq-sdk';
import { Hex, Address, maxUint256, createPublicClient, WalletClient, PublicClient} from 'viem';
import { privateKeyToAccount } from 'viem/accounts';
const privateKey = /* ... */;
const walletClient = createWalletClient({
account: privateKeyToAccount(privateKey),
chain: hardhat,
transport: custom(window.ethereum)
});
const intentClient = new IntentClient(walletClient, intentPoolURL);
Additionally, Permit2 authorization is required before the first transaction to permit :
intentClient.approvePermit2(tokenAddress: Address);
Issuing Swaps
The process of issuing a SwapIntent with the SDK is very simple. The IntentClient class provides a direct wrapper function for submitting intent messages to a GRPC intentpool such as UnEx.
Issuing a SwapIntent requires:
input and output addresses
input and output chains
input and output assets
import { IntentClient, getSupportedChainByID, getTestnetTokensByChainID, createItem } from '@fabriqnetwork/sdk';
import { createWalletClient, Hex, http, WalletClient, parseUnits } from 'viem';
import { optimism, sei } from 'viem/chains';
import { privateKeyToAccount } from 'viem/accounts';
const privateKey = /* ... */;
const walletClientOptimism: WalletClient = createWalletClient({
account: privateKeyToAccount(privateKey),
chain: optimism,
transport: http(),
});
const optimismInfo = await getSupportedChainByID(optimism.id);
const tokensOptimism = await getTokensByChainID(optimism.id);
const tokensSei = await getTokensByChainID(sei.id);
const intentClient = new IntentClient(walletClientOptimism, intentPoolURL, optimismInfo);
await intentClient.approvePermit2(tokensOp.Ceres);
const inAmount = parseUnits('2.00', 18);
const outAmount = parseUnits('1.98', 18);
const input = createItem(tokensOp.Ceres, inAmount, inAmount, optimism.id);
const output = createItem(tokensSei.Ceres, outAmount, outAmount, sei.id)
const intentId = await intentClient.submitSwapIntent(input, [output]);
Once the intent has been sent to UnEx, the SDK provides a function to easily report the status of an intent:
const swap = intentClient.getSwapIntent(intentId);
console.log(swap.status);
// `pending`, `registered`, `finalized` or `expired`
This will either report that the status of the intent is pending
, registered
, finalized
, or expired
(finalized and expired intents are only kept temporarily).
Querying Swaps
Querying intents is the first step as a potential counterparty. There are a few useful querying functions such as:
By Origin and/or Destination
By Origin and/or Destination Token Types
const swaps = await intentClient.querySwaps();
// all fields in the filter interface are optional
filter = { originChainIds = [ /* ... */ ],
destinationChainIds = [ /* ... */ ],
originTokens = [ /* ... */ ],
destinationTokens = [ /* ... */ ]
}
const filteredSwaps = await intentClient.querySwaps(filter);
The following example gets all intents originating from Optimism and destined for Sei, using the filter interface
op_filter = { originChainIds = [ optimism.id ],
destinationChainIds = [ sei.id ]
}
const filteredSwaps = await intentClient.querySwaps(op_filter);
Auction and Registration On-chain
To promote optimal routing, all potential counterparties participate in a dutch auction competing to fulfill swap intents. The first to register the intent on-chain and commit is the winner. The counterparty will need to register it on-chain.
This example first filters the intents by chain, uses a
const filteredSwaps = await intentClient.getSwaps(filter);
const desirableSwaps = selectSwaps(filteredSwaps);
var wonSwaps = []
for (const swap of desirableSwaps) {
const response = await intentClient.registerSwapIntent(swapIntent);
if (response.ok) {
wonIntents.push(swapIntent);
}
}
Fulfilling a SwapIntent and Finalization
The SDK currently does not support fulfillment from the APIs it provides. For this example, we complete fulfillment with viem
provided APIs.
// get a Swap as an example
const swap = wonSwaps[0];
// Sender's private key
const fillerPrivateKey = /* ... */;
async function transferExampleToken() {
const walletClient = createWalletClient({
account: privateKeyToAccount(fillerPrivateKey),
chain: sei.id,
transport: http(sei.url),
});
const txHash = await walletClient.writeContract({
abi: ERC20_ABI,
address: exampleTokenAddress,
functionName: 'transfer',
args: [swap.destinationAddress, swap.destinationAmount] });
console.log('Transaction Hash:', txHash);
// finalize
}
transferExampleToken().catch(console.error);
The last step in a swaps lifecycle, finalization, which releases the input tokens to the filler, is handled automatically by the oracle system. No interaction from the SDK is needed at this point.
Last updated