@georgeroman/wyvern-v2-sdk
Version:
Wyvern V2 SDK
312 lines • 12.9 kB
JavaScript
"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