Building a Swap
To get started building your first swap, you’ll need to install a few things. Assuming you’ve setup a TypeScript and Webpack bundling config (see details on requirements), you can then install dependencies. We install the main @sundaeswap/core
package, and then the latest version of @blaze-cardano/sdk
(we’re using Bun.sh here, but you can use whatever package manager you like):
$ bun add @sundaeswap/core @blaze-cardano/sdk
Steps
Building a swap consists of a few steps:
- Querying Pool Information
- Configuring your Swap Arguments
- Requesting a Signature & Submitting
Let’s take each step and break it down so we can understand what’s going on behind the scenes.
1. Querying Pool Information
For our example, we’ll be using the Preview network, and a pool ident for ADA/RBERRY. If you’re testing this locally, make sure you have Eternl installed and are on the preview
network and have at least 30 tADA in your wallet.
Keep in mind that async/await should usually be handled inside a function unless you have top-level await enabled in your bundler. For our purposes, they will not be in functions for clarity.
import { Blaze, Blockfrost, WebWallet } from "@blaze-cardano/sdk";
import { ETxBuilderType, ISundaeSDKOptions, SundaeSDK } from "@sundaeswap/core";
// Get the API from the browser window.
const myBlockfrostApiKey = "";
const api = await window.cardano.eternl.enable();
const blazeInstance = await Blaze.from(
new Blockfrost({
network: network ? "cardano-mainnet" : "cardano-preview",
projectId: myBlockfrostApiKey
}),
new WebWallet(api),
);
const options: ISundaeSDKOptions = {
wallet: {
name: "eternl",
network: "preview",
builder: {
blaze: blazeInstance,
type: ETxBuilderType.BLAZE,
},
},
};
const SDK = await new SundaeSDK(options);
const poolData = await SDK.query().findPoolData({
ident: "34c2b9d553d3a74b3ac67cc8cefb423af0f77fc84664420090e09990",
});
- The first thing we do is setup a Lucid instance. This is because our
options.builder
config is set to use theETxBuilderType.LUCID
type. - Next, we construct our SDK options config. We define that we’ll be using the Eternl wallet (defined as
eternl
to match the property on thewindow.cardano
object), and set the network topreview
. - Then, we instantiate a new
SundaeSDK
instance. It is recommended to keep this instance as a singleton across your app, so that it’s not recreated every time. - Finally, we query the
SDK.query()
method to get our defaultQueryProvider
, which includes a convenience method for fetching pool data directly from the SundaeSwap API.
2. Configuring Your Swap Arguments
Before moving on, let’s add a couple new imports to our file:
import { AssetAmount } from "@sundaeswap/asset";
import {
// ...the previous imports
ESwapType,
EDatumType,
ISwapConfigArgs,
} from "@sundaeswap/core";
- The
AssetAmount
class is a tool for dealing with asset calculations with regard to their respective decimal places and metadata. - The
ESwapType
,ISwapConfigArgs
, andEDatumType
will be used inside our configuration object.
Next, let’s build our swap configuration object:
// ...rest of our previous code
const args: ISwapConfigArgs = {
swapType: {
type: ESwapType.MARKET,
slippage: 0.03,
},
pool: poolData,
orderAddresses: {
DestinationAddress: {
address: "...your address",
datum: {
type: EDatumType.NONE,
},
},
},
suppliedAsset: new AssetAmount(25_000_000n, poolData.assetA),
};
- The
swapType
can be more than one type (see documentation), but in our case we are setting it to a market order, with a slippage of 0.3% (represented as a number). - The
poolData
we queried before is added as the pool we’re swapping against. - The
OrderAddresses.DestinationAddress
is just as it sounds: the destination of the scooped swap. Conceptually, the initial order submission will be deposited in a type of escrow account (a smart contract). From there, any on of the SundaeSwap scoopers will process this order, and send the result to whatever we set in this field. In our case, we don’t need to attach a datum, since it is going to our wallet address. - The
suppliedAsset
is how much of our wallet’s asset we are supplying to the pool. In our case, we want to supply 25 tADA (assetA
of thepoolData
object), and receive the matching RBERRY with an acceptable slippage tolerance of 0.03%.
3. Requesting a Signature & Submitting
Finally, once we have our configuration all set for the swap, we can build the transaction. Since we’re swapping against a V3 pool, let’s import the required types:
import {
// ...rest of imports
EContractVersion,
} from "@sundaeswap/core";
// ...rest of our code
const { build, fees } = await SDK.builder(EContractVersion.V3).swap(args);
- Here we select the
SDK.builder()
property. By default, the builder uses V1 contracts, so we pass inEContractVersion.V3
as a parameter. - From there, we can access the
.swap()
method, and pass in our configured args from the previous step. - The returned
build
property is what we will use in the next bit of code to actually build the transaction. - The returned
fees
property is what you can use to get visibility into scooper fees, deposits, and possible referrals (tutorial upcoming).
Next, let’s go ahead and build the transaction and request a signature:
// ... rest of our code
const builtTx = await build();
const { submit, cbor } = await builtTx.sign();
const txHash = await submit();
- First, we
build()
the transaction. This removes the details from memory, so manipulating the transaction details after this point is impossible aside form signing it. - Once we have a built transaction, we can access two things. 1) The
submit
method does what it sounds like: it submits the signed transaction transaction and returns the transaction hash for your record keeping and lookup. 2) Thecbor
property is the signed transaction in its fullness, which can then be used for many other things, including future support for multi-sig orders, manual entry to a different node, etc.
And that’s it! Congratulations on your first manually built order on the SundaeSwap protocol. Your submitted transaction with ensured datum correctness is now in the queue, and you should receive your corresponding RBERRY within a minute on preview during normal operation.