UNPKG

@georgeroman/wyvern-v2-sdk

Version:

Wyvern V2 SDK

312 lines 12.9 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 solidity_1 = require("@ethersproject/solidity"); const merkletreejs_1 = __importDefault(require("merkletreejs")); 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")); const PROVIDE_ROOT_REPLACEMENT_PATTERN = (numMerkleTreeLevels) => // "root" field "0".repeat(64) + // "proof" field // - location "0".repeat(64) + // - length "0".repeat(64) + // - actual items "f".repeat(64).repeat(numMerkleTreeLevels); const PROVIDE_PROOF_REPLACEMENT_PATTERN = (numMerkleTreeLevels) => // "root" field "f".repeat(64) + // "proof" field // - location "0".repeat(64) + // - length "0".repeat(64) + // - actual items "0".repeat(64).repeat(numMerkleTreeLevels); const REPLACEMENT_PATTERN_BUY = (numTokens, checkTokenId) => { const numMerkleTreeLevels = Math.ceil(Math.log2(numTokens)); return ( // "transferFrom" 4byte signature "0x00000000" + // "from" field "f".repeat(64) + // "to" field "0".repeat(64) + // "tokenId" field (checkTokenId ? "f".repeat(64) : "0".repeat(64)) + // merkle tree/proof verification data (checkTokenId ? PROVIDE_ROOT_REPLACEMENT_PATTERN(numMerkleTreeLevels) : PROVIDE_PROOF_REPLACEMENT_PATTERN(numMerkleTreeLevels))); }; const REPLACEMENT_PATTERN_SELL = (numTokens, checkTokenId) => { const numMerkleTreeLevels = Math.ceil(Math.log2(numTokens)); return ( // "transferFrom" 4byte signature "0x00000000" + // "from" field "0".repeat(64) + // "to" field "f".repeat(64) + // "tokenId" field (checkTokenId ? "f".repeat(64) : "0".repeat(64)) + // merkle tree/proof verification data (checkTokenId ? PROVIDE_ROOT_REPLACEMENT_PATTERN(numMerkleTreeLevels) : PROVIDE_PROOF_REPLACEMENT_PATTERN(numMerkleTreeLevels))); }; const getMerkleTree = (tokenIds) => { const hashFn = (buffer) => Buffer.from((0, solidity_1.keccak256)(["bytes"], [buffer]).slice(2), "hex"); const leaves = tokenIds.map((tokenId) => Buffer.from((0, solidity_1.keccak256)(["uint256"], [tokenId]).slice(2), "hex")); return new merkletreejs_1.default(leaves, hashFn, { sortPairs: true, }); }; const generateMerkleProof = (merkleTree, tokenId) => { const leaf = Buffer.from((0, solidity_1.keccak256)(["uint256"], [tokenId]).slice(2), "hex"); const proof = merkleTree.getHexProof(leaf); if (proof.length === 0) { throw new Error("Could not generate merkle proof"); } else { const numMerkleTreeLevels = merkleTree.getDepth(); if (proof.length < numMerkleTreeLevels - 1) { proof.push("0x" + "0".repeat(64)); } } return proof; }; const PROVIDE_ROOT_CALLDATA = (tokenIds) => { const numMerkleTreeLevels = Math.ceil(Math.log2(tokenIds.length)); const merkleTree = getMerkleTree(tokenIds); return ( // "root" field merkleTree.getHexRoot().slice(2) + // "proof" field // - location abi_1.defaultAbiCoder.encode(["uint256"], [64]).slice(2) + // - length abi_1.defaultAbiCoder.encode(["uint256"], [numMerkleTreeLevels]).slice(2) + // - actual items "0".repeat(64).repeat(numMerkleTreeLevels)); }; const PROVIDE_PROOF_CALLDATA = (tokenIds, proofTokenId) => { const numMerkleTreeLevels = Math.ceil(Math.log2(tokenIds.length)); const merkleTree = getMerkleTree(tokenIds); return ( // "root" field "0".repeat(64) + // "proof" field // - location abi_1.defaultAbiCoder.encode(["uint256"], [64]).slice(2) + // - length abi_1.defaultAbiCoder.encode(["uint256"], [numMerkleTreeLevels]).slice(2) + // - actual items generateMerkleProof(merkleTree, proofTokenId) .map((proof) => proof.slice(2)) .join("")); }; class AnyItemErc721OrderBuilder { // --------------- Public --------------- 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 sell(params) { const calldata = new abi_1.Interface(Erc721_json_1.default).encodeFunctionData("transferFrom", [ params.maker, constants_1.AddressZero, constants_1.AddressZero, ]) + PROVIDE_ROOT_CALLDATA(params.tokenIds); const replacementPattern = REPLACEMENT_PATTERN_SELL(params.tokenIds.length, true); const staticExtradata = // "verify" 4byte signature "0x8e760afe" + // location of the "_calldata" argument in the calldata abi_1.defaultAbiCoder.encode(["uint256"], [32]).slice(2) + // length of the "_calldata" argument abi_1.defaultAbiCoder .encode(["uint256"], [calldata.slice(2).length / 2]) .slice(2); 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: types_1.SaleKind.FIXED_PRICE, target: params.target, howToCall: types_1.HowToCall.CALL, calldata, replacementPattern, staticTarget: params.staticTarget, staticExtradata, paymentToken: params.paymentToken, basePrice: params.basePrice, 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, }); } static buy(params) { const calldata = new abi_1.Interface(Erc721_json_1.default).encodeFunctionData("transferFrom", [ constants_1.AddressZero, params.maker, constants_1.AddressZero, ]) + PROVIDE_ROOT_CALLDATA(params.tokenIds); const replacementPattern = REPLACEMENT_PATTERN_BUY(params.tokenIds.length, true); const staticExtradata = // "verify" 4byte signature "0x8e760afe" + // location of the "_calldata" argument in the calldata abi_1.defaultAbiCoder.encode(["uint256"], [32]).slice(2) + // length of the "_calldata" argument abi_1.defaultAbiCoder .encode(["uint256"], [calldata.slice(2).length / 2]) .slice(2); 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, saleKind: types_1.SaleKind.FIXED_PRICE, target: params.target, howToCall: types_1.HowToCall.CALL, calldata, replacementPattern, staticTarget: params.staticTarget, staticExtradata, paymentToken: params.paymentToken, basePrice: params.basePrice, 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, }); } static matchingSell(seller, buyOrder, boughtTokenIds, soldTokenId) { if (buyOrder.side !== types_1.Side.BUY) { throw new Error("Invalid buy order side"); } if (buyOrder.replacementPattern !== REPLACEMENT_PATTERN_BUY(boughtTokenIds.length, true)) { throw new Error("Invalid buy order replacement pattern"); } const buyCalldata = new abi_1.Interface(Erc721_json_1.default).decodeFunctionData("transferFrom", buyOrder.calldata); // Make sure the sell order's calldata (the beginning portion of it) has the following format: // "transferFrom(address(0), buyer, tokenId)" if (buyCalldata.to.toLowerCase() !== buyOrder.maker.toLowerCase()) { throw new Error("Invalid buy order calldata"); } if (buyCalldata.from !== constants_1.AddressZero) { throw new Error("Invalid buy order calldata"); } const calldata = new abi_1.Interface(Erc721_json_1.default).encodeFunctionData("transferFrom", [ seller, constants_1.AddressZero, soldTokenId, ]) + PROVIDE_PROOF_CALLDATA(boughtTokenIds, soldTokenId); const replacementPattern = REPLACEMENT_PATTERN_SELL(boughtTokenIds.length, false); 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, replacementPattern, 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, }); } static matchingBuy(buyer, sellOrder, soldTokenIds, boughtTokenId) { if (sellOrder.side !== types_1.Side.SELL) { throw new Error("Invalid sell order side"); } if (sellOrder.replacementPattern !== REPLACEMENT_PATTERN_SELL(soldTokenIds.length, true)) { throw new Error("Invalid sell order replacement pattern"); } const sellCalldata = new abi_1.Interface(Erc721_json_1.default).decodeFunctionData("transferFrom", sellOrder.calldata); // Make sure the sell order's calldata (the beginning portion of it) has the following format: // "transferFrom(seller, address(0), tokenId)" if (sellCalldata.from.toLowerCase() !== sellOrder.maker.toLowerCase()) { throw new Error("Invalid sell order calldata"); } if (sellCalldata.to !== constants_1.AddressZero) { throw new Error("Invalid sell order calldata"); } const calldata = new abi_1.Interface(Erc721_json_1.default).encodeFunctionData("transferFrom", [ constants_1.AddressZero, buyer, boughtTokenId, ]) + PROVIDE_PROOF_CALLDATA(soldTokenIds, boughtTokenId); const replacementPattern = REPLACEMENT_PATTERN_BUY(soldTokenIds.length, false); 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, replacementPattern, staticTarget: constants_1.AddressZero, staticExtradata: "0x", paymentToken: sellOrder.paymentToken, basePrice: sellOrder.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, }); } } exports.default = AnyItemErc721OrderBuilder; //# sourceMappingURL=any-item.js.map