@@ -23,6 +23,7 @@ import type {
2323import { UnexpectedAssetTypeError } from '../error' ;
2424import {
2525 approveAsset ,
26+ checkIfContractWallet ,
2627 DEFAULT_APP_ID ,
2728 generateErc1155Order ,
2829 generateErc721Order ,
@@ -34,6 +35,7 @@ import {
3435import type {
3536 AddressesForChainV4 ,
3637 ApprovalOverrides ,
38+ AvailableSignatureTypesV4 ,
3739 ERC721OrderStruct ,
3840 FillOrderOverrides ,
3941 NftOrderV4 ,
@@ -97,8 +99,6 @@ export const SupportedChainsForV4OrderbookStatusMonitoring = [
9799export interface INftSwapV4 extends BaseNftSwap {
98100 signOrder : (
99101 order : NftOrderV4 ,
100- signerAddress : string ,
101- signer : Signer ,
102102 signingOptions ?: Partial < SigningOptionsV4 >
103103 ) => Promise < SignedNftOrderV4 > ;
104104 buildNftAndErc20Order : (
@@ -601,32 +601,85 @@ class NftSwapV4 implements INftSwapV4 {
601601 * Once signed, the order becomes fillable (as long as the order is valid)
602602 * 0x orders require a signature to fill.
603603 * @param order A 0x v4 order
604+ * @param signingOptions Options for signing
604605 * @returns A signed 0x v4 order
605606 */
606- signOrder = async ( order : NftOrderV4 ) : Promise < SignedNftOrderV4 > => {
607+ signOrder = async (
608+ order : NftOrderV4 ,
609+ signingOptions ?: Partial < SigningOptionsV4 >
610+ ) : Promise < SignedNftOrderV4 > => {
607611 if ( ! this . signer ) {
608- throw new Error ( 'Signed not defined' ) ;
612+ throw new Error ( 'Signer not defined' ) ;
609613 }
610614
611- const rawSignature = await signOrderWithEoaWallet (
612- order ,
613- this . signer as unknown as TypedDataSigner ,
614- this . chainId ,
615- this . exchangeProxy . address
616- ) ;
615+ let method : AvailableSignatureTypesV4 = 'eoa' ;
616+ // If we have any specific signature type overrides, prefer those
617+ if ( signingOptions ?. signatureType === 'eip1271' ) {
618+ method = 'eip1271' ;
619+ } else if ( signingOptions ?. signatureType === 'eoa' ) {
620+ method = 'eoa' ;
621+ } else {
622+ // Try to detect...
623+ if ( signingOptions ?. autodetectSignatureType === false ) {
624+ method = 'eoa' ;
625+ } else {
626+ // If we made it here, consumer has no preferred signing method,
627+ // let's try feature detection to automagically pick a signature type
628+ // By default we fallback to EOA signing if we can't figure it out.
617629
618- const ecSignature = parseRawSignature ( rawSignature ) ;
630+ // Let's try to determine if the signer is a contract wallet or not.
631+ // If it is, we'll try EIP-1271, otherwise we'll do a normal sign
632+ const signerAddress = await this . signer . getAddress ( ) ;
619633
620- const signedOrder = {
621- ...order ,
622- signature : {
623- signatureType : 2 ,
624- r : ecSignature . r ,
625- s : ecSignature . s ,
626- v : ecSignature . v ,
627- } ,
628- } ;
629- return signedOrder ;
634+ const isContractWallet = await checkIfContractWallet (
635+ this . provider ,
636+ signerAddress
637+ ) ;
638+ if ( isContractWallet ) {
639+ method = 'eip1271' ;
640+ } else {
641+ method = 'eoa' ;
642+ }
643+ }
644+ }
645+ switch ( method ) {
646+ case 'eoa' : {
647+ const rawSignature = await signOrderWithEoaWallet (
648+ order ,
649+ this . signer as unknown as TypedDataSigner ,
650+ this . chainId ,
651+ this . exchangeProxy . address
652+ ) ;
653+
654+ const ecSignature = parseRawSignature ( rawSignature ) ;
655+
656+ const signedOrder = {
657+ ...order ,
658+ signature : {
659+ signatureType : 2 ,
660+ r : ecSignature . r ,
661+ s : ecSignature . s ,
662+ v : ecSignature . v ,
663+ } ,
664+ } ;
665+ return signedOrder ;
666+ }
667+ case 'eip1271' : {
668+ await this . preSignOrder ( order ) ;
669+ const preSignedOrder = {
670+ ...order ,
671+ signature : {
672+ signatureType : 4 ,
673+ r : '0' ,
674+ s : '0' ,
675+ v : 0 ,
676+ } ,
677+ } ;
678+ return preSignedOrder ;
679+ }
680+ default :
681+ throw new Error ( `Unknown signature method chosen: ${ method } ` ) ;
682+ }
630683 } ;
631684
632685 /**
@@ -862,6 +915,69 @@ class NftSwapV4 implements INftSwapV4 {
862915 console . log ( 'unsupported order' , signedOrder ) ;
863916 throw new Error ( 'unsupport signedOrder type' ) ;
864917 } ;
918+ /**
919+ * Pre-signs and submits an order
920+ * @param signedOrder An unsigned 0x v4 order
921+ * @param fillOrderOverrides Optional configuration on possible ways to fill the order
922+ * @param transactionOverrides Ethers transaction overrides (e.g. gas price)
923+ * @returns
924+ */
925+ preSignOrder = async (
926+ order : NftOrderV4 ,
927+ fillOrderOverrides ?: Partial < FillOrderOverrides > ,
928+ transactionOverrides ?: Partial < PayableOverrides >
929+ ) => {
930+ // Only Sell orders can be filled with ETH
931+ const canOrderTypeBeFilledWithNativeToken =
932+ order . direction === TradeDirection . SellNFT ;
933+ // Is ERC20 being traded the native token
934+ const isNativeToken = this . isErc20NativeToken ( order ) ;
935+ const needsEthAttached =
936+ isNativeToken && canOrderTypeBeFilledWithNativeToken ;
937+ if ( needsEthAttached ) {
938+ console . log (
939+ "can't pre-sign orders that need to be filled with ETH" ,
940+ order
941+ ) ;
942+ throw new Error ( "can't pre-sign orders that need to be filled with ETH" ) ;
943+ }
944+ // do fill
945+ if ( 'erc1155Token' in order ) {
946+ // If maker is selling an NFT, taker wants to 'buy' nft
947+ if (
948+ order . direction === TradeDirection . BuyNFT &&
949+ order . erc1155TokenProperties . length > 0 &&
950+ fillOrderOverrides ?. tokenIdToSellForCollectionOrder === undefined
951+ ) {
952+ // property based order, let's make sure they've specifically provided a tokenIdToSellForCollectionOrder
953+ throw new Error (
954+ 'Collection order missing NFT tokenId to fill with. Specify in fillOrderOverrides.tokenIdToSellForCollectionOrder'
955+ ) ;
956+ }
957+
958+ return this . exchangeProxy . preSignERC1155Order ( order , {
959+ ...transactionOverrides ,
960+ } ) ;
961+ } else if ( 'erc721Token' in order ) {
962+ // If maker is selling an NFT, taker wants to 'buy' nft
963+ if (
964+ order . direction === TradeDirection . BuyNFT &&
965+ order . erc721TokenProperties . length > 0 &&
966+ fillOrderOverrides ?. tokenIdToSellForCollectionOrder === undefined
967+ ) {
968+ // property based order, let's make sure they've specifically provided a tokenIdToSellForCollectionOrder
969+ throw new Error (
970+ 'Collection order missing NFT tokenId to fill with. Specify in fillOrderOverrides.tokenIdToSellForCollectionOrder'
971+ ) ;
972+ }
973+ // Otherwise, taker is selling the nft (and buying an ERC20)
974+ return this . exchangeProxy . preSignERC721Order ( order , {
975+ ...transactionOverrides ,
976+ } ) ;
977+ }
978+ console . log ( 'unsupported order' , order ) ;
979+ throw new Error ( 'unsupport signedOrder type' ) ;
980+ } ;
865981
866982 /**
867983 * Posts a 0x order to the Trader.xyz NFT open orderbook
0 commit comments