UNPKG

@georgeroman/wyvern-v2-sdk

Version:

Wyvern V2 SDK

252 lines 11 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const abi_1 = require("@ethersproject/abi"); const constants_1 = require("@ethersproject/constants"); const order_1 = __importDefault(require("../../helpers/order")); const types_1 = require("../../types"); const utils_1 = require("../../utils"); const Erc721_json_1 = __importDefault(require("../../abis/Erc721.json")); // ERC721 calldata format: // transferFrom(from, to, tokenId) // ERC721 replacement pattern format: // 4 bytes - "transferFrom" function signature // 64 bytes - "from" // 64 bytes - "to" // 64 bytes - "tokenId" const REPLACEMENT_PATTERN_BUY = "0x00000000" + "f".repeat(64) + "0".repeat(64) + "0".repeat(64); const REPLACEMENT_PATTERN_SELL = "0x00000000" + "0".repeat(64) + "f".repeat(64) + "0".repeat(64); class SingleItemErc721OrderBuilder { static extractTokenId(order) { try { const result = new abi_1.Interface(Erc721_json_1.default).decodeFunctionData("transferFrom", order.calldata); return result.tokenId.toString(); } catch { return undefined; } } static isWellFormatted(order) { try { const tokenId = this.extractTokenId(order); if (!tokenId) { return false; } // Build a mock order that is for sure well-formatted let mock; if (order.side === types_1.Side.SELL) { mock = this.sell({ ...order, tokenId, fee: "0", }); } else if (order.side === types_1.Side.BUY) { mock = this.buy({ ...order, tokenId, fee: "0", }); } if (!mock) { return false; } if (Number(order.makerRelayerFee) > 10000 || Number(order.takerRelayerFee) > 10000) { return false; } mock.taker = order.taker; mock.makerRelayerFee = order.makerRelayerFee; mock.takerRelayerFee = order.takerRelayerFee; // Make sure the two orders exactly match return order_1.default.hash(mock) === order_1.default.hash(order); } catch { return false; } } static sell(params) { try { const isDutchAuction = (0, utils_1.bn)(params.extra || 0).gt(0); if (isDutchAuction) { if ((0, utils_1.bn)(params.listingTime).gte((0, utils_1.bn)(params.expirationTime))) { throw new Error("Invalid listing/expiration time"); } } return order_1.default.normalize({ exchange: params.exchange, maker: params.maker, taker: constants_1.AddressZero, makerRelayerFee: params.fee, takerRelayerFee: "0", feeRecipient: params.feeRecipient, side: types_1.Side.SELL, saleKind: isDutchAuction ? types_1.SaleKind.DUTCH_AUCTION : types_1.SaleKind.FIXED_PRICE, target: params.target, howToCall: types_1.HowToCall.CALL, calldata: new abi_1.Interface(Erc721_json_1.default).encodeFunctionData("transferFrom", [ params.maker, constants_1.AddressZero, params.tokenId, ]), replacementPattern: REPLACEMENT_PATTERN_SELL, staticTarget: constants_1.AddressZero, staticExtradata: "0x", paymentToken: params.paymentToken, basePrice: params.basePrice, extra: params.extra || "0", listingTime: params.listingTime, expirationTime: params.expirationTime, salt: params.salt, v: params.v || 0, r: params.r || utils_1.Bytes32Zero, s: params.s || utils_1.Bytes32Zero, }); } catch { return undefined; } } static buy(params) { try { return order_1.default.normalize({ exchange: params.exchange, maker: params.maker, taker: constants_1.AddressZero, makerRelayerFee: "0", takerRelayerFee: params.fee, feeRecipient: params.feeRecipient, side: types_1.Side.BUY, // Notice: we don't support dutch auction buys saleKind: types_1.SaleKind.FIXED_PRICE, target: params.target, howToCall: types_1.HowToCall.CALL, calldata: new abi_1.Interface(Erc721_json_1.default).encodeFunctionData("transferFrom", [ constants_1.AddressZero, params.maker, params.tokenId, ]), replacementPattern: REPLACEMENT_PATTERN_BUY, staticTarget: constants_1.AddressZero, staticExtradata: "0x", paymentToken: params.paymentToken, basePrice: params.basePrice, extra: params.extra || "0", listingTime: params.listingTime, expirationTime: params.expirationTime, salt: params.salt, v: params.v || 0, r: params.r || utils_1.Bytes32Zero, s: params.s || utils_1.Bytes32Zero, }); } catch { return undefined; } } static matchingSell(seller, buyOrder) { try { if (buyOrder.side !== types_1.Side.BUY) { throw new Error("Invalid buy order side"); } if (buyOrder.replacementPattern !== REPLACEMENT_PATTERN_BUY) { throw new Error("Invalid buy order replacement pattern"); } // Notice: we don't support dutch auction buys if (buyOrder.saleKind !== types_1.SaleKind.FIXED_PRICE) { throw new Error("Unsupported sale kind"); } // The buy order's calldata must have the following format: // "transferFrom(address(0), buyer, tokenId)" const buyCalldata = new abi_1.Interface(Erc721_json_1.default).decodeFunctionData("transferFrom", buyOrder.calldata); if (buyCalldata.to.toLowerCase() !== buyOrder.maker.toLowerCase()) { throw new Error("Invalid buy order calldata"); } return order_1.default.normalize({ exchange: buyOrder.exchange, maker: seller, taker: buyOrder.maker, makerRelayerFee: buyOrder.makerRelayerFee, takerRelayerFee: buyOrder.takerRelayerFee, feeRecipient: constants_1.AddressZero, side: types_1.Side.SELL, saleKind: types_1.SaleKind.FIXED_PRICE, target: buyOrder.target, howToCall: types_1.HowToCall.CALL, calldata: new abi_1.Interface(Erc721_json_1.default).encodeFunctionData("transferFrom", [seller, constants_1.AddressZero, buyCalldata.tokenId]), replacementPattern: REPLACEMENT_PATTERN_SELL, staticTarget: constants_1.AddressZero, staticExtradata: "0x", paymentToken: buyOrder.paymentToken, basePrice: buyOrder.basePrice, extra: "0", listingTime: (0, utils_1.getListingTime)().toString(), expirationTime: "0", salt: (0, utils_1.getSalt)().toString(), v: 0, r: utils_1.Bytes32Zero, s: utils_1.Bytes32Zero, }); } catch { return undefined; } } static matchingBuy(buyer, sellOrder) { try { if (sellOrder.side !== types_1.Side.SELL) { throw new Error("Invalid sell order side"); } if (sellOrder.replacementPattern !== REPLACEMENT_PATTERN_SELL) { throw new Error("Invalid sell order replacement pattern"); } // The sell order's calldata must have the following format: // "transferFrom(seller, address(0), tokenId)" const sellCalldata = new abi_1.Interface(Erc721_json_1.default).decodeFunctionData("transferFrom", sellOrder.calldata); if (sellCalldata.from.toLowerCase() !== sellOrder.maker.toLowerCase()) { throw new Error("Invalid buy order calldata"); } // As in https://github.com/ProjectWyvern/wyvern-ethereum/blob/bfca101b2407e4938398fccd8d1c485394db7e01/contracts/exchange/SaleKindInterface.sol#L70-L87 const listingTime = (0, utils_1.getListingTime)(); const basePrice = sellOrder.saleKind === types_1.SaleKind.FIXED_PRICE ? (0, utils_1.bn)(sellOrder.basePrice) : (0, utils_1.bn)(sellOrder.basePrice).sub((0, utils_1.bn)(sellOrder.extra) .mul(listingTime.sub((0, utils_1.bn)(sellOrder.listingTime))) .div((0, utils_1.bn)(sellOrder.expirationTime).sub((0, utils_1.bn)(sellOrder.listingTime)))); return order_1.default.normalize({ exchange: sellOrder.exchange, maker: buyer, taker: sellOrder.maker, makerRelayerFee: sellOrder.makerRelayerFee, takerRelayerFee: sellOrder.takerRelayerFee, feeRecipient: constants_1.AddressZero, side: types_1.Side.BUY, saleKind: types_1.SaleKind.FIXED_PRICE, target: sellOrder.target, howToCall: types_1.HowToCall.CALL, calldata: new abi_1.Interface(Erc721_json_1.default).encodeFunctionData("transferFrom", [constants_1.AddressZero, buyer, sellCalldata.tokenId]), replacementPattern: REPLACEMENT_PATTERN_BUY, staticTarget: constants_1.AddressZero, staticExtradata: "0x", paymentToken: sellOrder.paymentToken, basePrice: basePrice.toString(), extra: "0", listingTime: listingTime.toString(), expirationTime: "0", salt: (0, utils_1.getSalt)().toString(), v: 0, r: utils_1.Bytes32Zero, s: utils_1.Bytes32Zero, }); } catch { return undefined; } } } exports.default = SingleItemErc721OrderBuilder; //# sourceMappingURL=single-item.js.map