This SDK aims to make it easy for web apps to integrate MetaStreet leverage trading features. It’s composed of 3 packages:
pe-contracts-typechain
: this is a very basic package that contains MetaStreet Purchase Escrow (pe) contracts ABIs, and their corresponding typechain generated code.margin-core
: this package provides functions that let you interact with MetaStreet’s PE contracts, either to send transactions or read on-chain data, which is easier than usingpe-contracts-typechain
. The package also contains functions to easily query the subgraph, as well as all the needed contract addresses and API URLs.margin-kit
: this is a React JS package, it contains ready-to-use components that can be plugged into any React app to integrate MetaStreet’s leveraged trading features in a few lines of code. It also contains building blocks that you can use to build your own custom components.
We built a demo app to showcase SDK features, you can check the code under apps/test, or visit the hosted version. You can also see how the SDK is implemented in a full-fledged marketplace on our PowerSweep (On Görli) product.
- Install the package and it's peer dependencies:
npm i @metastreet-labs/margin-kit ethers react react-dom @tanstack/react-query
oryarn add @metastreet-labs/margin-kit ethers react react-dom @tanstack/react-query
- import the CSS file
import "@metastreet-labs/margin-kit/styles/default.css”
- wrap your React app in a
MetaStreetConfig
, passing in all the required web3 configuration.MetaStreetConfig
also lets you customize the subgraph URIs. You can use the default subgraph URI for Goerli, but for mainnet, you have to either pass in your own URI, or use ours but with your API key, example:
import "@metastreet-labs/margin-kit/styles/default.css"
import { defaultMainnetSubgraphURI, MetaStreetConfig } from "@metastreet-labs/margin-kit";
const MyApp = () => {
// you can use defaultMainnetSubgraphURI with your own API key
let mainnetSubgraphURI = defaultMainnetSubgraphURI("your api key");
// or you can use your own subgraph URI
mainnetSubgraphURI = "my subgraph uri";
return (
<MetaStreetConfig
{...required web3 config}
subgraphs={{
mainnet: mainnetSubgraphURI,
// the default Goerli URI should work fine, but feel free to change it if you need to
//goerli: "",
}}
>
{Your app...}
</MetaStreetConfig>
);
};
Take a look at our DemoMetaStreetConfig for an idea on how to pass the required params. It uses wagmi, but you can use whatever wallet connection library you want.
npm i @metastreet-labs/margin-core
or
yarn add @metastreet-labs/margin-core
npm i @metastreet-labs/pe-contracts-typechain
or
yarn add @metastreet-labs/pe-contracts-typechain
MetaStreet lets you buy NFTs with leverage, you pay a down payment and the rest is borrowed from MetaStreet vaults, then the NFT is purchased and put into MetaStreet’s escrow contracts. After that, you can either:
- Repay: you repay the loan, and the NFT will be transferred to you.
- Refinance: you extend the leverage (…explain more)
- List for Sale: you list the NFT for sale, and when it’s sold, the loan is repaid from the purchase price, and you receive whatever’s left.
Users can buy one or multiple NFTs with leverage, they select the debt amount
and duration
of the loan, and they will be quoted a repayment
. Once they’re happy with the loan terms, they can proceed with the purchase.
The NFTs need to be supported by Reservoir, i.e be listed in one of Reservoir’s whitelisted marketplaces.
margin-kit
offers out-of-the-box React components that handle buying with leverage:
BuyWithLeverageButton
: it takes an array ofBWLTokens
, and is either disabled with an error message if the passed-in tokens can’t be purchased with leverage (check the codebase to see what checks are being made), or enabled if the tokens can be purchased, in which case the passed inonClick
callback should show the UI where the user selects the loan terms. TheBuyWithLeverageButton
component is styled and MetaStreet branded, if you want to customize it, you can use theuseBuyWithLeverageButton
hook. Check the code forBuyWithLeverageButton
as an example.BuyWithLeverageModal
: it also takes an array ofBWLTokens
to be purchased, users can select the loandebt
andduration
, and arepayment
will be quoted for them, using eitherquoteSingleERC721
orquoteMultipleERC721
functions from MetaStreet’s LeverageBuyWrapper contract. The modal also shows different information about the loan, which is mainly calculated fromdebt
,duration
, andrepayment
. Once the user is happy with the terms, they can hit the buy button, which will initiate the buy with leverage transaction, calling eitherbuySingleERC721WithETH
orbuyMultipleERC721WithETH
contract functions. Again, if you’re not happy with the styling of the modal, you can either try to modify the CSS file or build a completely customized component. If you chose to build it from scratch, you can use theBuyWithLeverageProvider
component which does the heavy lifting and handles all of the state management.BuyWithLeverage
: this is just a component that wraps the aboveBuyWithLeverageButton
andBuyWithLeverageModal
.
The demo app has a fully functional Buy With Leverage implementation, check out the code.
After buying an NFT with leverage, users can manage their leverage positions by interacting with MetaSreet’s LeverageBuyWrapper smart contracts.
In order to manage loans, we first need to fetch them. That’s done using MetaStreet’s Leverage Buy Subgraph (see subgraph section). You can either:
- Write your own GraphQL queries
- Use the fetchers from
margin-core
if you don’t want to write GraphQL - Use the hooks from
margin-kit
, they work out-of-the-box for Goerli, but you’ll have to provide an API key, or a different subgraph URI for mainnet. Your custom config should be passed toDeploymentProvider
.
The loans are represented as LeverageBuy
objects and used as inputs for different functions, we’ll go through them in the next sections.
PS: the subgraph also contains a LeverageBuyEvent entity, representing the different events that happened to a LeverageBuy position (refinanced, repaid, listed for sale…etc).
Check out the PositionsSection
component of the demo app to see how we fetch LeverageBuy
entities of the connected address, and display them in a table.
A loan can be refinanced to extend its leverage/duration, the mechanism is the same for buying with leverage, users select a new debt
and duration
, and a new repayment
will be quoted for them,
margin-kit
offers a modal component that handles refinancing out-of-the-box:
RefinanceModal
: it takes aLeverageBuy
object, users can select a newdebt
andduration
, and a newrepayment
will be quoted for them using thequoteRefinance
contract function. The modal shows different information about the new loan, the same as with theBuyWithLeverageModal
. Once the user is happy with the new terms, they can hit the Refinance button, triggering the refinance transaction, by calling therefiannceETH
function from MetaStreet’sPurchaseEscrowPlatform
contract. If you’re not happy with the styling of the modal, you can either try to modify the CSS file or build a completely customized component. If you chose to build it from scratch, you can use theRefinanceProvider
component which does the heavy lifting and handles all of the state management.
Check out how the RefinanceModal
is used inside the LBRow
component of the demo app
Users can list their loan’s underlying NFT for sale on OpenSea, and when the NFT is sold, the loan is repaid from the selling price, and users get whatever’s left. This means that users can’t list their NFTs for a price less than the loan’s repayment (plus fees and royalties).
margin-kit
offers a modal component that handles listing for sale:
ListForSaleModal
: it takes aLeverageBuy
object and apostOrderToOpensea
callback that you have to define (more on this below), users can enter their listing price using a number input which is pre-populated with the minimum listing price. The modal also displays different information about the loan, and how much the user will gain/lose after the NFT is sold at the selected price. If you’re not happy with the styling of the modal, you can either try to modify the CSS file or build a completely customized component. If you chose to build it from scratch, you can use theListForSaleProvider
component which does the heavy lifting and handles all of the state management. -postOrderToOpensea(order: Order)
: this is a function that you have to write, it should make a call to OpenSea API to post the order off-chain. This is necessary for the listing to be visible on OpenSea’s frontend. It should make a POST request to this endpoint for mainnet, or this one for Goerli. Check out theNotListedLB
component of the demo app for an example on how to do it for Goerli. The reason why we didn’t handle this on the SDK is because the mainnet endpoint requires an OpenSea API key, so the request has to be done server-side.
The listing duration is set to 7 days, after that it will expire and users will have to re-list. There’s a cancelListing
function on the PurchaseEscrowPlatform
contract that can be used to cancel a listing before it expires, you can use the useCancelListing
hook from margin-kit
.
Check out the demo app for an example on how to list an NFT for sale (NotListedLB
component) and how to cancel a listing (ListedLB
component).
Users can choose to repay their loans, in which case the underlying NFT will be transferred to their address. This is done by calling repayETH
on the PurchaseEscrowPlatform
contract. You can use the useRepayETH
hook from margin-kit
, check out the RepayButton
component of the demo app for an example.
MetaStreet SDK is MIT licensed.