This SDK facilitates interactions with both EVM and non-EVM networks, supporting cross-chain swaps, on-chain exchanges, and bridging functionalities. It is designed for developers building decentralized applications requiring seamless blockchain integrations.
- EVM and non-EVM Network Support: Unified interfaces for handling blockchain operations across Ethereum Virtual Machine (EVM) and other networks.
- Cross-Chain Transactions: Simplifies bridging tokens and assets between different networks.
- Multi-Call Optimization: Efficiently batches calls to minimize on-chain interactions and reduce gas costs.
- Dynamic Network Configuration: Supports multiple networks with easily configurable settings.
- Comprehensive Simulation: Simulates routes and transactions before execution.
- Strongly Typed with TypeScript: Ensures type safety and reduces runtime errors.
Install the SDK using npm or yarn:
npm install @safeblock/exchange-sdk
# or
yarn add @safeblock/exchange-sdk
import { SafeBlock } from "@safeblock/exchange-sdk"
const sdk = new SafeBlock({
tokensList: {}, // Optional, initial tokens list
// Optional, default is 20, will skip all routes that has price impact
// more than 20 percents
routePriceDifferenceLimit: 20,
backend: {
url: "https://api.safeblock.com",
headers: {} // Optional, setup authorization headers if needed
},
// Optional
priceStorage: {
// Optional, time in milliseconds between price update cycles
updateInterval: 15_000
}
})
import { Address, Amount, bnb } from "@safeblock/blockchain-utils"
const request = {
tokenIn: { network: bnb, address: Address.from("0xTokenInAddress"), decimals: 18 },
tokenOut: { network: bnb, address: Address.from("0xTokenOutAddress"), decimals: 18 },
amountIn: Amount.from(1, 18, true),
amountOut: Amount.from(0, 18, true), // Any amount, because exactInput is true
exactInput: true,
slippageReadablePercent: 1 // e.g. 1%
}
const routes = await sdk.findRoutes(request) // => SdkError or list of routes
A quota is a representation of a swap that contains all the data for the upcoming swap and a ready calldata for the transaction
const route = routes[0] // Select a route from the simulation results
const quota = await sdk.createQuotaFromRoute(Address.from("0xYourAddress"), route) // => Error or quota
If you don't need detailed route management, you can simplify the code and get quota right away:
import { Address } from "@safeblock/blockchain-utils"
const request = {
/* Same as in previous example */
}
const task = sdk.updateTask()
const quota = await sdk.createQuota(Address.from("0xYourAddress"), request, task)
This method will automatically calculate the routes and return you the best quota possible
If you use the ethers library to interact with contracts, you can easily prepare transactions for posting using SDK:
const quota: ExchangeQuota = {} // From previous example
const signer = JsonRpcSigner // Your ethers signer
for (const data of quota.executorCallData) {
const transaction = await sdk.prepareEthersTransaction(data, signer)
// In case of any errors, the prepareEthersTransaction method will return
// a standard SdkException error
if (transaction instanceof SdkException) {
console.log("Error occured", transaction.code, transaction.message)
return
}
const tx = signer.sendTransaction(transaction)
console.log("Transaction sent:", tx.hash)
const receipt = await tx.wait(1)
}
Warning. Most likely, the transaction array will contain an approval transaction, so it is strongly recommended to wait for the execution of the first transaction before starting the execution of subsequent ones to avoid simulation errors
If you prefer to use any other library besides ethers, you can just as easily build a transaction to send manually:
// quota and signer variables are the same as in previous example
for (const data of quota.executorCallData) {
// There is the basic transaction params
const transactionData = data.callData // Transaction data
const network = data.network.chainId // Chain id of the network where transaction should be sent to
const value = data.value // Native amount, may be undefined
const to = data.to // Destination address
// First, you need to estimate transaction gas consumption
const gasEstimation = 1 // Replace with actual logic
// Now you need to get current fee data or only gasPrice
const gasPrice = 1 // Replace with actual logic
// In this example we used BigNumber, but you can use anything you like
const gasLimit = new BigNumber(estimation).multipliedBy(callData.gasLimitMultiplier ?? 1).toFixed(0)
// With data gathered above we are now able to send transaction, just
// pack it with your library and sign with a wallet
}
SDK supports the ability to subscribe to specific events that occur under the hood:
Event name | Description |
---|---|
initialized | Will be called after completing the receipt of balances for the first time |
pricesUpdated | Will be called every time the prices in priceStorage are updated |
tokenAdded | Will be called after the token is added to tokensList |
tokenRemoved | Will be called after the token is removed from the tokensList |
Usage example:
import { Address, bnb } from "@safeblock/blockchain-utils"
// Lets create logic that will log wBNB price right after SDK priceStorage initialization
sdk.addEventListener("initialized", sdk => {
const network = bnb
// Get price of wrapped BNB token
const price = sdk.priceStorage.getPrice(network, Address.wrappedOf(network))
// In this case, if the token is added to the SDK, the price is unambiguously received
console.log("wBNB price:", price.toReadableBigNumber().toFixed())
})
// To remove specific listener use following syntax
sdk.removeEventListener("initialized", /* listener */)
// You can also remove all listeners of specific event...
sdk.removeEventListener("initialized")
// ... or event remove all listeners
sdk.cleanEventListeners()
Tests are located in the __specs__
folder and can be run using Vitest:
npm run test
This project is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
For more information, visit: CC BY-NC-SA 4.0.