2024-06-13 13:09:43 +08:00

274 lines
10 KiB
JavaScript

import { config, passport, orderbook, checkout } from '@imtbl/sdk';
import { createSingleton } from '@/utils/singleton';
import { providers } from 'ethers';
const environment = process.env.NODE_ENV === 'production' ? config.Environment.PRODUCTION : config.Environment.SANDBOX;
const publishableKey = import.meta.env.VUE_APP_PASSPORT_PUBLISHABLE_KEY
const clientId = import.meta.env.VUE_APP_PASSPORT_CLIENT_ID
const redirectUri = import.meta.env.VUE_APP_PASSPORT_REDIRECT_URI
const logoutRedirectUri = import.meta.env.VUE_APP_PASSPORT_LOGOUT_URI
const marketAddress = import.meta.env.VUE_APP_PASSPORT_MARKET_ADDRESS
const NATIVE = 'NATIVE'
const ERC20 = 'ERC20'
const baseConfig = { environment, publishableKey }
class LPassportWallet {
constructor() {
this.passportInstance = new passport.Passport({
baseConfig,
clientId, // replace with your client ID from Hub
redirectUri, // replace with one of your redirect URIs from Hub
logoutRedirectUri, // replace with one of your logout URIs from Hub
audience: 'platform_api',
scope: 'openid offline_access email transact',
popupOverlayOptions: {
disableGenericPopupOverlay: false, // Set to true to disable the generic pop-up overlay
disableBlockedPopupOverlay: false, // Set to true to disable the blocked pop-up overlay
}
});
this.passportInstance.loginCallback();
this.client = new orderbook.Orderbook({ baseConfig });
}
async initWidget() {
const checkoutSDK = new checkout.Checkout({
baseConfig,
passport: this.passportInstance,
bridge: { enable: true },
swap: { enable: true },
onRamp: { enable: true }
});
const widgets = await checkoutSDK.widgets({
config: { theme: checkout.WidgetTheme.DARK },
});
// RECOMMENDED - create all of the widgets once at the start of your application
// use the created widgets throughout your application to mount and unmount in specific parts of your application
const connect = widgets.create(checkout.WidgetType.CONNECT);
const wallet = widgets.create(checkout.WidgetType.WALLET); // you can optionally pass in additional config per widget
const swap = widgets.create(checkout.WidgetType.SWAP);
const bridge = widgets.create(checkout.WidgetType.BRIDGE);
const onramp = widgets.create(checkout.WidgetType.ONRAMP);
// Mount the wallet widget passing the element id of where to mount the widget
connect.mount('wallet');
}
async connect() {
const profile = await this.passportInstance.login();
console.log(profile,'-----------------------------------------------------------------')
const passportProvider = this.passportInstance.connectEvm();
this.web3Provider = new providers.Web3Provider(passportProvider);
this.signer = this.web3Provider.getSigner();
const accounts = await passportProvider.request({ method: "eth_requestAccounts" })
const accessToken = await this.passportInstance.getAccessToken();
console.log('accessToken:', accessToken)
// const idToken = await passportInstance.getIdToken();
// console.log('idToken:', idToken)
return {profile, accounts, accessToken}
}
async logout() {
await this.passportInstance.logout();
}
/**
* 查询某个nft的所有挂单
* @param {*} contractAddress
* @returns
*/
async listListings(contractAddress){
const listOfListings = await this.client.listListings({
sellItemContractAddress: contractAddress,
status: orderbook.OrderStatusName.ACTIVE,
pageSize: 50,
});
return listOfListings;
};
/**
* 准备一个ERC721的挂单
* @returns
*/
async _prepareERC721Listing({ contractAddress, tokenId, type = 'ERC721', currencyAddress, currencyAmount}){
const offerer = await this.signer.getAddress();
const buyData = {
amount: currencyAmount,
}
if (currencyAddress == NATIVE) {
buyData.type = NATIVE
} else {
buyData.type = ERC20
buyData.contractAddress = currencyAddress
}
const preparedListing = await this.client.prepareListing({
makerAddress: offerer,
buy: buyData,
sell: {
contractAddress,
tokenId,
type,
},
});
let orderSignature = ''
for (const action of preparedListing.actions) {
// If the user hasn't yet approved the Immutable Seaport contract to transfer assets from this
// collection on their behalf they'll need to do so before they create an order
if (action.type === orderbook.ActionType.TRANSACTION) {
const builtTx = await action.buildTransaction()
console.log(`Submitting ${action.purpose} transaction`)
await signer.sendTransaction(builtTx);
}
// For an order to be created (and subsequently filled), Immutable needs a valid signature for the order data.
// This signature is stored off-chain and is later provided to any user wishing to fulfil the open order.
// The signature only allows the order to be fulfilled if it meets the conditions specified by the user that created the listing.
if (action.type === orderbook.ActionType.SIGNABLE) {
orderSignature = await signer._signTypedData(
action.message.domain,
action.message.types,
action.message.value,
)
}
}
return { preparedListing, orderSignature }
}
/**
* 创建一个挂单
* @param {*} preparedListing
* @param {*} orderSignature
*/
async _createListing(
preparedListing,
orderSignature,
currencyAmount
){
const amount = (BigInt(currencyAmount) * 2n / 100n).toString()
const order = await this.client.createListing({
orderComponents: preparedListing.orderComponents,
orderHash: preparedListing.orderHash,
orderSignature,
// Optional maker marketplace fee
makerFees: [{
amount,
recipientAddress: marketAddress, // Replace address with your own marketplace address
}],
});
console.log('order:', order);
return order
};
/**
* 出售一个ERC721的NFT
* doc: https://docs.immutable.com/docs/zkEVM/products/orderbook/create-listing
* @param {string} contractAddress NFT的合约地址
* @param {string} tokenId NFT的tokenId
* @param {string} currencyAddress NATIVE 或者 ERC20的合约地址
* @param {string} currencyAmount 出售价格, 单位 wei
*/
async beginSellERC721({contractAddress, tokenId, currencyAddress, currencyAmount}) {
const { preparedListing, orderSignature } =
await this._prepareERC721Listing({contractAddress, tokenId, currencyAddress, currencyAmount});
const order = await this._createListing(preparedListing, orderSignature, currencyAmount);
return order
}
/**
* 开始购买
* doc: https://docs.immutable.com/docs/zkEVM/products/orderbook/fill
* @param {*} listingId
*/
async beginBuy(listingId) {
const fulfiller = await this.signer.getAddress();
const { actions, expiration, order } = await this.client.fulfillOrder(
listingId,
fulfiller,
[]
);
console.log(`Fulfilling listing ${order}, transaction expiry ${expiration}`);
for (const action of actions) {
if (action.type === orderbook.ActionType.TRANSACTION) {
const builtTx = await action.buildTransaction();
console.log(`Submitting ${action.purpose} transaction`);
await signer.sendTransaction(builtTx);
}
}
}
/**
* 批量购买
* doc: https://docs.immutable.com/docs/zkEVM/products/orderbook/fill-bulk
* @param { string[] } listingIds: listingId列表
*/
async batchBuy(listingIds) {
const fulfiller = await this.signer.getAddress();
try {
const fulfillResponse = await this.client.fulfillBulkOrders(
listingIds.map((listingId) => ({
listingId,
// you could have up to 2 marketplace fees
takerFees: [],
})),
fulfiller
);
if (fulfillResponse.sufficientBalance) {
const { actions, expiration, fulfillableOrders, unfulfillableOrders } = fulfillResponse;
// depending on the application, we can either throw an error if some orders are not fulfillable
// or we can ignore these unfulfillable orders and proceed with fulfillment
if (unfulfillableOrders.length > 0) {
throw new Error(
`Not all orders are fulfillable - unfulfillable orders: ${unfulfillableOrders}`
);
}
for (const action of actions) {
if (action.type === orderbook.ActionType.TRANSACTION) {
const builtTx = await action.buildTransaction();
console.log(`Submitting ${action.purpose} transaction`);
await signer.sendTransaction(builtTx);
}
}
console.log(
`Fulfilling listings ${fulfillableOrders}, transaction expiry ${expiration}`
);
}
} catch (e) {
console.error(`Fulfill bulk orders request failed with ${e}`);
throw e;
}
}
/**
* 取消交易
* doc: https://docs.immutable.com/docs/zkEVM/products/orderbook/cancel
* @param {*} listingIds
* @returns
*/
async cancelOrder(listingIds) {
const account = await this.signer.getAddress();
const { signableAction } = await this.client.prepareOrderCancellations(listingIds);
const cancellationSignature = await this.signer._signTypedData(
signableAction.message.domain,
signableAction.message.types,
signableAction.message.value,
)
return this.client.cancelOrders(listingIds, account, cancellationSignature)
}
/**
* 取消交易, onChain
* doc: https://docs.immutable.com/docs/zkEVM/products/orderbook/cancel
* @param {*} listingIds
* @returns
*/
async cancelOrdersOnChain(listingIds) {
const offerer = await this.signer.getAddress();
const { cancellationAction } = await this.client.cancelOrdersOnChain(
listingIds,
offerer
);
const unsignedCancelOrderTransaction = await cancellationAction.buildTransaction();
const receipt = await this.signer.sendTransaction(unsignedCancelOrderTransaction);
return receipt;
}
}
export const PassportWallet = createSingleton(LPassportWallet)