Skip to content

Commit 32a12a4

Browse files
authored
Merge pull request #24 from trader-xyz/feat/no-approvals-for-accepting-nft-bid
No approvals required when taker sells NFT by accepting a bid
2 parents fb617c0 + c021f84 commit 32a12a4

File tree

6 files changed

+511
-9
lines changed

6 files changed

+511
-9
lines changed

src/sdk/v4/NftSwapV4.ts

Lines changed: 107 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,17 @@
1-
import { Signer, TypedDataSigner } from '@ethersproject/abstract-signer';
2-
import { BaseProvider, TransactionReceipt } from '@ethersproject/providers';
3-
import { BigNumber, BigNumberish, ContractTransaction } from 'ethers';
4-
import { IZeroEx, IZeroEx__factory } from '../../contracts';
1+
import type { Signer, TypedDataSigner } from '@ethersproject/abstract-signer';
2+
import type {
3+
BaseProvider,
4+
TransactionReceipt,
5+
} from '@ethersproject/providers';
6+
import type { BigNumberish, ContractTransaction } from 'ethers';
7+
import { Interface } from '@ethersproject/abi';
8+
import invariant from 'tiny-invariant';
9+
import {
10+
ERC1155__factory,
11+
ERC721__factory,
12+
IZeroEx,
13+
IZeroEx__factory,
14+
} from '../../contracts';
515
import type {
616
ApprovalStatus,
717
BaseNftSwap,
@@ -36,9 +46,11 @@ import type {
3646
SignedNftOrderV4,
3747
SigningOptions,
3848
} from './types';
49+
import {
50+
ERC1155_TRANSFER_FROM_DATA,
51+
ERC721_TRANSFER_FROM_DATA,
52+
} from './nft-safe-transfer-from-data';
3953
import addresses from './addresses.json';
40-
import invariant from 'tiny-invariant';
41-
import { NULL_ADDRESS } from '../../utils/eth';
4254

4355
export enum SupportedChainIdsV4 {
4456
Ropsten = 3,
@@ -364,6 +376,95 @@ class NftSwapV4 implements INftSwapV4 {
364376
return signedOrder;
365377
};
366378

379+
/**
380+
* Fill a 'Buy NFT' order (e.g. taker would be selling'their NFT to fill this order) without needing an approval
381+
* Use case: Users can accept offers/bids for their NFTs without needing to approve their NFT! 🤯
382+
* @param signedOrder Signed Buy Nft order (e.g. direction = 1)
383+
* @param tokenId NFT token id that taker of trade will sell
384+
* @param fillOrderOverrides Trade specific (SDK-level) overrides
385+
* @param transactionOverrides General transaction overrides from ethers (gasPrice, gasLimit, etc)
386+
* @returns
387+
*/
388+
fillBuyNftOrderWithoutApproval = async (
389+
signedOrder: SignedNftOrderV4,
390+
tokenId: string,
391+
fillOrderOverrides?: Partial<FillOrderOverrides>,
392+
transactionOverrides?: Partial<PayableOverrides>
393+
) => {
394+
if (!this.signer) {
395+
throw new Error(
396+
'Signer undefined. Signer must be provided to fill order'
397+
);
398+
}
399+
if (signedOrder.direction !== TradeDirection.BuyNFT) {
400+
throw new Error(
401+
'Only filling Buy NFT orders (direction=1) is valid for skipping approvals'
402+
);
403+
}
404+
405+
const signerAddress = await this.signer.getAddress();
406+
const unwrapWeth =
407+
fillOrderOverrides?.fillOrderWithNativeTokenInsteadOfWrappedToken ??
408+
false;
409+
410+
// Handle ERC721
411+
if ('erc721Token' in signedOrder) {
412+
const erc721Contract = ERC721__factory.connect(
413+
signedOrder.erc721Token,
414+
this.signer
415+
);
416+
417+
const encodingIface = new Interface(ERC721_TRANSFER_FROM_DATA);
418+
419+
const fragment = encodingIface.getFunction('safeTransferFromErc721Data');
420+
const data = encodingIface._encodeParams(fragment.inputs, [
421+
signedOrder,
422+
signedOrder.signature,
423+
unwrapWeth,
424+
]);
425+
426+
const transferFromTx = await erc721Contract[
427+
'safeTransferFrom(address,address,uint256,bytes)'
428+
](
429+
signerAddress,
430+
this.exchangeProxy.address,
431+
fillOrderOverrides?.tokenIdToSellForCollectionOrder ?? tokenId,
432+
data,
433+
transactionOverrides ?? {}
434+
);
435+
return transferFromTx;
436+
}
437+
438+
// Handle ERC1155
439+
if ('erc1155Token' in signedOrder) {
440+
const erc1155Contract = ERC1155__factory.connect(
441+
signedOrder.erc1155Token,
442+
this.signer
443+
);
444+
const encodingIface = new Interface(ERC1155_TRANSFER_FROM_DATA);
445+
446+
const fragment = encodingIface.getFunction('safeTransferFromErc1155Data');
447+
const data = encodingIface._encodeParams(fragment.inputs, [
448+
signedOrder,
449+
signedOrder.signature,
450+
unwrapWeth,
451+
]);
452+
453+
const transferFromTx = await erc1155Contract.safeTransferFrom(
454+
signerAddress,
455+
this.exchangeProxy.address,
456+
fillOrderOverrides?.tokenIdToSellForCollectionOrder ?? tokenId,
457+
signedOrder.erc1155TokenAmount ?? '1',
458+
data,
459+
transactionOverrides ?? {}
460+
);
461+
return transferFromTx;
462+
}
463+
464+
// Unknown format (NFT neither ERC721 or ERC1155)
465+
throw new Error('unknown order type');
466+
};
467+
367468
fillSignedCollectionOrder = async (
368469
signedOrder: SignedNftOrderV4,
369470
tokenId: BigNumberish,

0 commit comments

Comments
 (0)