Skip to content

Commit

Permalink
fix: update the checkout.Sell interface to allow undefined type prope…
Browse files Browse the repository at this point in the history
…rty (#1798)
  • Loading branch information
Sam-Jeston authored May 20, 2024
1 parent 48c40ea commit 8d3c503
Show file tree
Hide file tree
Showing 3 changed files with 214 additions and 11 deletions.
178 changes: 178 additions & 0 deletions packages/checkout/sdk/src/smartCheckout/sell/sell.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,184 @@ describe('sell', () => {
});

describe('sell', () => {
it('should call smart checkout and execute the transactions for a deprecated typed ERC721 order', async () => {
const id = '0';
const contractAddress = '0xERC721';

const erc721ItemRequirement = {
type: ItemType.ERC721,
id,
contractAddress,
spenderAddress: seaportContractAddress,
};

const erc721TransactionRequirement = {
type: ItemType.ERC721,
sufficient: true,
required: {
type: ItemType.ERC721,
balance: BigNumber.from(1),
formattedBalance: '1',
contractAddress: '0xab8bb5bc4FB1Cfc060f77f87B558c98abDa65130',
id: '0',
},
current: {
type: ItemType.ERC721,
balance: BigNumber.from(1),
formattedBalance: '1',
contractAddress: '0xab8bb5bc4FB1Cfc060f77f87B558c98abDa65130',
id: '0',
},
delta: {
balance: BigNumber.from(0),
formattedBalance: '0',
},
};

(smartCheckout as jest.Mock).mockResolvedValue({
sufficient: true,
transactionRequirements: [
erc721TransactionRequirement,
],
});

const mockCreateListing = jest.fn().mockResolvedValue({
result: {
id: '1234',
},
});

const prepareListing = jest.fn().mockResolvedValue({
actions: [
{
type: ActionType.SIGNABLE,
purpose: SignablePurpose.CREATE_LISTING,
message: {
domain: '',
types: '',
value: '',
},
},
],
});

(createOrderbookInstance as jest.Mock).mockReturnValue({
config: jest.fn().mockReturnValue({
seaportContractAddress,
}),
prepareListing,
createListing: mockCreateListing,
});

(getUnsignedMessage as jest.Mock).mockReturnValue(
{
orderHash: 'hash',
orderComponents: {},
unsignedMessage: {
domain: {} as TypedDataDomain,
types: { types: [] },
value: { values: '' },
},
},
);
(signMessage as jest.Mock).mockResolvedValue({
orderHash: 'hash',
orderComponents: {},
signedMessage: '0xSIGNED',
});
(getUnsignedSellTransactions as jest.Mock).mockResolvedValue({
approvalTransactions: [{ from: '0xAPPROVAL' }],
});
(signApprovalTransactions as jest.Mock).mockResolvedValue({
type: SignTransactionStatusType.SUCCESS,
});

const orderExpiry = new Date('2022-03-25');
const order: SellOrder = {
sellToken: {
id,
collectionAddress: contractAddress,
},
buyToken: {
type: ItemType.NATIVE,
amount: '1',
},
makerFees: [{
amount: { percentageDecimal: 0.025 },
recipient: '0xEac347177DbA4a190B632C7d9b8da2AbfF57c772',
}],
orderExpiry,
};

const result = await sell(
config,
mockProvider,
[order],
);

expect(result).toEqual({
smartCheckoutResult: {
sufficient: true,
transactionRequirements: [erc721TransactionRequirement],
},
status: CheckoutStatus.SUCCESS,
orderIds: ['1234'],
});

expect(smartCheckout).toBeCalledWith(
config,
mockProvider,
[erc721ItemRequirement],
{
type: TransactionOrGasType.GAS,
gasToken: {
type: GasTokenType.NATIVE,
limit: BigNumber.from(constants.estimatedFulfillmentGasGwei),
},
},
);
expect(prepareListing).toBeCalledWith({
makerAddress: walletAddress,
buy: {
type: ItemType.NATIVE,
amount: '1000000000000000000',
},
sell: {
type: ItemType.ERC721,
contractAddress: order.sellToken.collectionAddress,
tokenId: order.sellToken.id,
},
orderExpiry: order.orderExpiry,
});
expect(signMessage).toBeCalledWith(
mockProvider,
{
orderHash: 'hash',
orderComponents: {},
unsignedMessage: {
domain: {} as TypedDataDomain,
types: { types: [] },
value: { values: '' },
},
},
);
expect(signApprovalTransactions).toBeCalledWith(
mockProvider,
[{ from: '0xAPPROVAL' }],
);
expect(mockCreateListing).toBeCalledWith(
{
makerFees: [{
amount: '25000000000000000',
recipientAddress: '0xEac347177DbA4a190B632C7d9b8da2AbfF57c772',
}],
orderComponents: {},
orderHash: 'hash',
orderSignature: '0xSIGNED',
},
);
});

it('should call smart checkout and execute the transactions for ERC721 order', async () => {
const id = '0';
const contractAddress = '0xERC721';
Expand Down
30 changes: 21 additions & 9 deletions packages/checkout/sdk/src/smartCheckout/sell/sell.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import {
Orderbook,
PrepareListingResponse,
constants,
ERC721Item as OrderbookERC721Item,
ERC1155Item as OrderbookERC1155Item,
} from '@imtbl/orderbook';
import { BigNumber, Contract, utils } from 'ethers';
import {
Expand Down Expand Up @@ -120,6 +122,7 @@ export const sell = async (
}

const buyTokenOrNative = getBuyToken(buyToken, decimals);
const sellTokenHasType = 'type' in sellToken;

try {
const walletAddress = await measureAsyncExecution<string>(
Expand All @@ -130,17 +133,18 @@ export const sell = async (
orderbook = instance.createOrderbookInstance(config);
const { seaportContractAddress } = orderbook.config();
spenderAddress = seaportContractAddress;
const sellItem = sellToken.type === ItemType.ERC721

const sellItem: OrderbookERC721Item | OrderbookERC1155Item = sellTokenHasType && sellToken.type === ItemType.ERC1155
? {
type: sellToken.type,
type: ItemType.ERC1155,
contractAddress: sellToken.collectionAddress,
tokenId: sellToken.id,
amount: sellToken.amount,
}
: {
type: sellToken.type,
type: ItemType.ERC721,
contractAddress: sellToken.collectionAddress,
tokenId: sellToken.id,
amount: sellToken.amount,
};

listing = await measureAsyncExecution<PrepareListingResponse>(
Expand All @@ -165,11 +169,19 @@ export const sell = async (
);
}

const itemRequirements = [
sellToken.type === ItemType.ERC721
? getERC721Requirement(sellToken.id, sellToken.collectionAddress, spenderAddress)
: getERC1155Requirement(sellToken.id, sellToken.collectionAddress, spenderAddress, sellToken.amount),
];
const itemRequirements: (ERC721Item | ERC1155Item)[] = [];
if (sellTokenHasType && sellToken.type === ItemType.ERC1155) {
const erc1155ItemRequirement = getERC1155Requirement(
sellToken.id,
sellToken.collectionAddress,
spenderAddress,
sellToken.amount,
);
itemRequirements.push(erc1155ItemRequirement);
} else {
const erc721ItemRequirement = getERC721Requirement(sellToken.id, sellToken.collectionAddress, spenderAddress);
itemRequirements.push(erc721ItemRequirement);
}

let smartCheckoutResult;
const isPassport = (provider.provider as any)?.isPassport;
Expand Down
17 changes: 15 additions & 2 deletions packages/checkout/sdk/src/types/smartCheckout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ export type BuyOrder = {
*/
export type SellOrder = {
/** the token to be listed for sale */
sellToken: ERC721SellToken | ERC1155SellToken;
sellToken: SellToken;
/** the token info of the price of the item */
buyToken: BuyToken;
/** optional array of makerFees to be applied to the listing */
Expand Down Expand Up @@ -316,7 +316,7 @@ export type ERC20BuyToken = {
* Represents the token listed for sale.
* ERC721SellToken or ERC1155SellToken {@link Checkout.smartCheckout}.
*/
export type SellToken = ERC721SellToken | ERC1155SellToken;
export type SellToken = DeprecatedERC721SellToken | ERC721SellToken | ERC1155SellToken;

/**
* The ERC721SellToken type
Expand All @@ -331,6 +331,19 @@ export type ERC721SellToken = {
collectionAddress: string;
};

/**
* The original ERC721SellToken type, before the introduction of the ItemType enum
* @property {string} id
* @property {string} collectionAddress
* @deprecated
*/
export type DeprecatedERC721SellToken = {
/** The ERC721 token id */
id: string;
/** The ERC721 collection address */
collectionAddress: string;
};

/**
* The ERC1155SellToken type
* @property {string} id
Expand Down

0 comments on commit 8d3c503

Please sign in to comment.