Skip to content

Commit

Permalink
Merge 8ba1480 into a00089c
Browse files Browse the repository at this point in the history
  • Loading branch information
JoshuaSchmidt-OpenSea committed Mar 29, 2023
2 parents a00089c + 8ba1480 commit 12abede
Show file tree
Hide file tree
Showing 4 changed files with 140 additions and 110 deletions.
2 changes: 2 additions & 0 deletions src/constants.ts
@@ -1,3 +1,4 @@
import BigNumber from "bignumber.js";
import { Network } from "./types";

export const DEFAULT_GAS_INCREASE_FACTOR = 1.01;
Expand Down Expand Up @@ -40,6 +41,7 @@ export const RPC_URL_PATH = "jsonrpc/v1/";
export const MAINNET_PROVIDER_URL = `${API_BASE_MAINNET}/${RPC_URL_PATH}`;
export const ORDERBOOK_PATH = `/wyvern/v${ORDERBOOK_VERSION}`;
export const API_PATH = `/api/v${ORDERBOOK_VERSION}`;
export const MAX_UINT_256 = new BigNumber(2).pow(256).minus(1);

export const EIP_712_ORDER_TYPES = {
EIP712Domain: [
Expand Down
124 changes: 15 additions & 109 deletions src/sdk.ts
Expand Up @@ -8,6 +8,7 @@ import {
CreateInputItem,
OrderComponents,
} from "@opensea/seaport-js/lib/types";
import { generateRandomSalt } from "@opensea/seaport-js/lib/utils/order";
import { BigNumber } from "bignumber.js";
import { Web3JsProvider } from "ethereum-types";
import { isValidAddress } from "ethereumjs-util";
Expand Down Expand Up @@ -43,6 +44,7 @@ import {
WRAPPED_NFT_LIQUIDATION_PROXY_ADDRESS_MAINNET,
WRAPPED_NFT_LIQUIDATION_PROXY_ADDRESS_RINKEBY,
OPENSEA_LEGACY_FEE_RECIPIENT,
MAX_UINT_256,
} from "./constants";
import {
CanonicalWETH,
Expand Down Expand Up @@ -137,6 +139,7 @@ import {
getAddressAfterRemappingSharedStorefrontAddressToLazyMintAdapterAddress,
feesToBasisPoints,
isValidProtocol,
toBaseUnitAmount,
} from "./utils/utils";

export { WyvernProtocol };
Expand Down Expand Up @@ -186,7 +189,6 @@ export class OpenSeaSDK {
// API config
apiConfig.networkName = apiConfig.networkName || Network.Main;
this.api = new OpenSeaAPI(apiConfig);
this._wyvernConfigOverride = apiConfig.wyvernConfig;

this._networkName = apiConfig.networkName;

Expand Down Expand Up @@ -572,7 +574,7 @@ export class OpenSeaSDK {
) as unknown as UniswapExchangeAbi;

// Convert desired WNFT to wei
const amount = WyvernProtocol.toBaseUnitAmount(
const amount = toBaseUnitAmount(
makeBigNumber(numTokens),
Number(wrappedNFT.methods.decimals().call())
);
Expand Down Expand Up @@ -610,10 +612,7 @@ export class OpenSeaSDK {
}) {
const token = getCanonicalWrappedEther(this._networkName);

const amount = WyvernProtocol.toBaseUnitAmount(
makeBigNumber(amountInEth),
token.decimals
);
const amount = toBaseUnitAmount(makeBigNumber(amountInEth), token.decimals);

this._dispatch(EventType.WrapEth, { accountAddress, amount });

Expand Down Expand Up @@ -649,10 +648,7 @@ export class OpenSeaSDK {
}) {
const token = getCanonicalWrappedEther(this._networkName);

const amount = WyvernProtocol.toBaseUnitAmount(
makeBigNumber(amountInEth),
token.decimals
);
const amount = toBaseUnitAmount(makeBigNumber(amountInEth), token.decimals);

this._dispatch(EventType.UnwrapWeth, { accountAddress, amount });

Expand Down Expand Up @@ -1264,90 +1260,6 @@ export class OpenSeaSDK {
);
}

/**
* Cancel an order on-chain, preventing it from ever being fulfilled.
* @param param0 __namedParameters Object
* @param order The order to cancel
* @param accountAddress The order maker's wallet address
*/
public async cancelOrderLegacyWyvern({
order,
accountAddress,
}: {
order: Order;
accountAddress: string;
}) {
this._dispatch(EventType.CancelOrder, { order, accountAddress });

const transactionHash = await this._wyvernProtocol.wyvernExchange
.cancelOrder_(
[
order.exchange,
order.maker,
order.taker,
order.feeRecipient,
order.target,
order.staticTarget,
order.paymentToken,
],
[
order.makerRelayerFee,
order.takerRelayerFee,
order.makerProtocolFee,
order.takerProtocolFee,
order.basePrice,
order.extra,
order.listingTime,
order.expirationTime,
order.salt,
],
order.feeMethod,
order.side,
order.saleKind,
order.howToCall,
order.calldata,
order.replacementPattern,
order.staticExtradata,
order.v || 0,
order.r || NULL_BLOCK_HASH,
order.s || NULL_BLOCK_HASH
)
.sendTransactionAsync({ from: accountAddress });

await this._confirmTransaction(
transactionHash,
EventType.CancelOrder,
"Cancelling order",
async () => {
const isOpen = await this._validateOrder(order);
return !isOpen;
}
);
}

/**
* Cancel all existing orders with a lower nonce on-chain, preventing them from ever being fulfilled.
* @param param0 __namedParameters Object
* @param accountAddress The order maker's wallet address
*/
public async bulkCancelExistingOrdersLegacyWyvern({
accountAddress,
}: {
accountAddress: string;
}) {
this._dispatch(EventType.BulkCancelExistingOrders, { accountAddress });

const transactionHash = await this._wyvernProtocol.wyvernExchange
.incrementNonce()
.sendTransactionAsync({ from: accountAddress });

await this._confirmTransaction(
transactionHash.toString(),
EventType.BulkCancelExistingOrders,
"Bulk cancelling existing orders"
);
}

/**
* Approve a non-fungible token for use in trades.
* Requires an account to be initialized first.
Expand Down Expand Up @@ -1572,7 +1484,7 @@ export class OpenSeaSDK {
accountAddress,
tokenAddress,
proxyAddress,
minimumAmount = WyvernProtocol.MAX_UINT_256,
minimumAmount = MAX_UINT_256,
}: {
accountAddress: string;
tokenAddress: string;
Expand Down Expand Up @@ -1627,7 +1539,7 @@ export class OpenSeaSDK {
getMethod(ERC20, "approve"),
// Always approve maximum amount, to prevent the need for followup
// transactions (and because old ERC20s like MANA/ENJ are non-compliant)
[proxyAddress, WyvernProtocol.MAX_UINT_256.toString()]
[proxyAddress, MAX_UINT_256.toString()]
),
},
(error) => {
Expand Down Expand Up @@ -1911,10 +1823,7 @@ export class OpenSeaSDK {
): Promise<boolean> {
const schema = this._getSchema(this._getSchemaName(asset));
const quantityBN = quantity
? WyvernProtocol.toBaseUnitAmount(
makeBigNumber(quantity),
asset.decimals || 0
)
? toBaseUnitAmount(makeBigNumber(quantity), asset.decimals || 0)
: makeBigNumber(1);
const wyAsset = getWyvernAsset(schema, asset, quantityBN);
const abi = schema.functions.transfer(wyAsset);
Expand Down Expand Up @@ -1975,7 +1884,7 @@ export class OpenSeaSDK {
quantity?: number | BigNumber;
}): Promise<string> {
const schema = this._getSchema(this._getSchemaName(asset));
const quantityBN = WyvernProtocol.toBaseUnitAmount(
const quantityBN = toBaseUnitAmount(
makeBigNumber(quantity),
asset.decimals || 0
);
Expand Down Expand Up @@ -2643,7 +2552,7 @@ export class OpenSeaSDK {
extra: makeBigNumber(0),
listingTime: times.listingTime,
expirationTime: times.expirationTime,
salt: WyvernProtocol.generatePseudoRandomSalt(),
salt: new BigNumber(generateRandomSalt()),
metadata: order.metadata,
};

Expand Down Expand Up @@ -3394,21 +3303,21 @@ export class OpenSeaSDK {
? makeBigNumber(
this.web3.utils.toWei(startAmount.toString(), "ether")
).integerValue()
: WyvernProtocol.toBaseUnitAmount(startAmount, token.decimals);
: toBaseUnitAmount(startAmount, token.decimals);

const endPrice = endAmount
? isEther
? makeBigNumber(
this.web3.utils.toWei(endAmount.toString(), "ether")
).integerValue()
: WyvernProtocol.toBaseUnitAmount(endAmount, token.decimals)
: toBaseUnitAmount(endAmount, token.decimals)
: undefined;

const extra = isEther
? makeBigNumber(
this.web3.utils.toWei(priceDiff.toString(), "ether")
).integerValue()
: WyvernProtocol.toBaseUnitAmount(priceDiff, token.decimals);
: toBaseUnitAmount(priceDiff, token.decimals);

const reservePrice = englishAuctionReservePrice
? isEther
Expand All @@ -3418,10 +3327,7 @@ export class OpenSeaSDK {
"ether"
)
).integerValue()
: WyvernProtocol.toBaseUnitAmount(
englishAuctionReservePrice,
token.decimals
)
: toBaseUnitAmount(englishAuctionReservePrice, token.decimals)
: undefined;

return { basePrice, extra, paymentToken, reservePrice, endPrice };
Expand Down
103 changes: 103 additions & 0 deletions src/utils/assert.ts
@@ -0,0 +1,103 @@
/* Sourced from 0x.js */

import { assert as sharedAssert } from "@0x/assert";
// We need those two unused imports because they're actually used by sharedAssert which gets injected here
// eslint-disable-next-line @typescript-eslint/no-unused-vars
import { schemas } from "@0x/json-schemas";
// eslint-disable-next-line @typescript-eslint/no-unused-vars
import { BigNumber } from "@0x/utils";
import { Web3Wrapper } from "@0x/web3-wrapper";
import * as ethUtil from "ethereumjs-util";
import * as _ from "lodash";
import { ECSignature } from "../types";

/* Sourced from: https://github.com/ProjectOpenSea/wyvern-js/blob/master/src/utils/assert.ts */
export const assert = {
...sharedAssert,
isValidSignature(
orderHash: string,
ecSignature: ECSignature,
signerAddress: string
) {
const isValidSignature = signatureUtils.isValidSignature(
orderHash,
ecSignature,
signerAddress
);
this.assert(
isValidSignature,
`Expected order with hash '${orderHash}' to have a valid signature`
);
},
async isSenderAddressAsync(
variableName: string,
senderAddressHex: string,
web3Wrapper: Web3Wrapper
): Promise<void> {
sharedAssert.isETHAddressHex(variableName, senderAddressHex);
const isSenderAddressAvailable =
await web3Wrapper.isSenderAddressAvailableAsync(senderAddressHex);
sharedAssert.assert(
isSenderAddressAvailable,
`Specified ${variableName} ${senderAddressHex} isn't available through the supplied web3 provider`
);
},
async isUserAddressAvailableAsync(web3Wrapper: Web3Wrapper): Promise<void> {
const availableAddresses = await web3Wrapper.getAvailableAddressesAsync();
this.assert(
!_.isEmpty(availableAddresses),
"No addresses were available on the provided web3 provider"
);
},
};

/* Sourced from https://github.com/ProjectOpenSea/wyvern-js/blob/master/src/utils/signature_utils.ts */
const signatureUtils = {
isValidSignature(
data: string,
signature: ECSignature,
signerAddress: string
): boolean {
const dataBuff = ethUtil.toBuffer(data);
// const msgHashBuff = ethUtil.hashPersonalMessage(dataBuff);
const msgHashBuff = dataBuff;
try {
const pubKey = ethUtil.ecrecover(
msgHashBuff,
signature.v,
ethUtil.toBuffer(signature.r),
ethUtil.toBuffer(signature.s)
);
const retrievedAddress = ethUtil.bufferToHex(
ethUtil.pubToAddress(pubKey)
);
return retrievedAddress === signerAddress;
} catch (err) {
return false;
}
},
parseSignatureHexAsVRS(signatureHex: string): ECSignature {
const signatureBuffer = ethUtil.toBuffer(signatureHex);
let v = +ethUtil.bufferToHex(signatureBuffer.slice(0, 1));
if (v < 27) {
v += 27;
}
const r = signatureBuffer.slice(1, 33);
const s = signatureBuffer.slice(33, 65);
const ecSignature: ECSignature = {
v,
r: ethUtil.bufferToHex(r),
s: ethUtil.bufferToHex(s),
};
return ecSignature;
},
parseSignatureHexAsRSV(signatureHex: string): ECSignature {
const { v, r, s } = ethUtil.fromRpcSig(signatureHex);
const ecSignature: ECSignature = {
v,
r: ethUtil.bufferToHex(r),
s: ethUtil.bufferToHex(s),
};
return ecSignature;
},
};
21 changes: 20 additions & 1 deletion src/utils/utils.ts
Expand Up @@ -14,6 +14,7 @@ import Web3 from "web3";
import { AbstractProvider } from "web3-core/types";
import { JsonRpcResponse } from "web3-core-helpers/types";
import { Contract } from "web3-eth-contract";
import { assert } from "./assert";
import { Schema } from "./schemas/schema";
import {
ENJIN_ADDRESS,
Expand Down Expand Up @@ -965,7 +966,7 @@ export function getOrderHash(order: UnhashedOrder) {
return getOrderHashHex(orderWithStringTypes as any);
}

// Copied from: https://github.com/ProjectOpenSea/wyvern-js/blob/master/src/utils/utils.ts#L39
// sourced from: https://github.com/ProjectOpenSea/wyvern-js/blob/master/src/utils/utils.ts#L39
function getOrderHashHex(order: UnhashedOrder): string {
const orderParts = [
{ value: order.exchange, type: SolidityTypes.Address },
Expand Down Expand Up @@ -1023,6 +1024,24 @@ function bigNumberToBN(value: BigNumber) {
return new BN(value.toString(), 10);
}

// Sourced from: https://github.com/ProjectOpenSea/wyvern-js/blob/master/src/wyvernProtocol.ts#L170
export function toBaseUnitAmount(
amount: BigNumber,
decimals: number
): BigNumber {
assert.isBigNumber("amount", amount);
assert.isNumber("decimals", decimals);
const unit = new BigNumber(10).pow(decimals);
const baseUnitAmount = amount.times(unit);
const hasDecimals = baseUnitAmount.decimalPlaces() !== 0;
if (hasDecimals) {
throw new Error(
`Invalid unit amount: ${amount.toString()} - Too many decimal places`
);
}
return baseUnitAmount;
}

/**
* Assign an order and a new matching order to their buy/sell sides
* @param order Original order
Expand Down

0 comments on commit 12abede

Please sign in to comment.