11import { ZERO_ADDRESS } from "./../../packages/core-sdk/tests/mocks" ;
22import { BigNumberish } from "@ethersproject/bignumber" ;
3+ import { parseEther } from "@ethersproject/units" ;
34import { Wallet , BigNumber , constants } from "ethers" ;
45import { OfferFieldsFragment } from "../../packages/core-sdk/src/subgraph" ;
56import { mockCreateOfferArgs } from "../../packages/common/tests/mocks" ;
@@ -26,12 +27,19 @@ import {
2627 createOfferWithCondition ,
2728 getCollectionMetadataUri ,
2829 createRandomWallet ,
29- META_TX_API_ID_VOUCHER
30+ META_TX_API_ID_VOUCHER ,
31+ createDisputeResolver ,
32+ deployerWallet ,
33+ initSellerAndBuyerSDKs
3034} from "./utils" ;
3135import { CoreSDK , forwarder } from "../../packages/core-sdk/src" ;
3236import EvaluationMethod from "../../contracts/protocol-contracts/scripts/domain/EvaluationMethod" ;
3337import TokenType from "../../contracts/protocol-contracts/scripts/domain/TokenType" ;
34- import { AuthTokenType , GatingType } from "../../packages/common" ;
38+ import { AuthTokenType , GatingType , OfferCreator } from "../../packages/common" ;
39+ import {
40+ MSEC_PER_DAY ,
41+ MSEC_PER_SEC
42+ } from "./../../packages/common/src/utils/timestamp" ;
3543
3644const sellerWallet = seedWallet7 ; // be sure the seedWallet is not used by another test (to allow concurrent run)
3745const sellerAddress = sellerWallet . address ;
@@ -724,6 +732,198 @@ describe("meta-tx", () => {
724732 } ) ;
725733 } ) ;
726734
735+ describe ( "#signMetaTxCommitToBuyerOffer()" , ( ) => {
736+ test ( "native exchange token buyer-initiated offer" , async ( ) => {
737+ const exchangeToken = constants . AddressZero ;
738+ // drFeeAmount must be 0 so the seller doesn't need to deposit funds before
739+ // committing via meta-tx (meta-tx can't carry ETH value)
740+ const drFeeAmount = "0" ;
741+
742+ // Create a dispute resolver with zero fee (required for meta-tx compatibility)
743+ const { fundedWallet : drFundedWallet } =
744+ await initCoreSDKWithFundedWallet ( sellerWallet ) ;
745+ const drAddress = drFundedWallet . address . toLowerCase ( ) ;
746+ const { disputeResolver } = await createDisputeResolver (
747+ drFundedWallet ,
748+ deployerWallet ,
749+ {
750+ assistant : drAddress ,
751+ admin : drAddress ,
752+ treasury : drAddress ,
753+ metadataUri : "" ,
754+ escalationResponsePeriodInMS :
755+ 90 * MSEC_PER_DAY - 1 * MSEC_PER_SEC ,
756+ fees : [
757+ {
758+ feeAmount : drFeeAmount ,
759+ tokenAddress : exchangeToken ,
760+ tokenName : "Native"
761+ }
762+ ] ,
763+ sellerAllowList : [ ]
764+ }
765+ ) ;
766+
767+ // Create fresh buyer/seller wallets
768+ const {
769+ sellerCoreSDK : sellerCoreSDKBuyer ,
770+ buyerCoreSDK : buyerCoreSDKBuyer ,
771+ sellerWallet : sellerFundedWallet
772+ } = await initSellerAndBuyerSDKs ( sellerWallet ) ;
773+
774+ // Buyer creates a buyer-initiated offer.
775+ // sellerDeposit must be 0 so the meta-tx relayer doesn't need to forward ETH value.
776+ const buyerInitiatedOffer = await createOffer ( buyerCoreSDKBuyer , {
777+ creator : OfferCreator . Buyer ,
778+ quantityAvailable : 1 ,
779+ disputeResolverId : disputeResolver . id ,
780+ exchangeToken,
781+ sellerDeposit : "0"
782+ } ) ;
783+
784+ // Buyer deposits offer.price to allow the seller to commit
785+ const buyerDepositTx = await buyerCoreSDKBuyer . depositFunds (
786+ buyerInitiatedOffer . buyerId ,
787+ buyerInitiatedOffer . price ,
788+ exchangeToken
789+ ) ;
790+ await buyerCoreSDKBuyer . waitForGraphNodeIndexing ( buyerDepositTx ) ;
791+
792+ // Seller creates a seller account
793+ await createSeller ( sellerCoreSDKBuyer , sellerFundedWallet . address ) ;
794+ // No seller depositFunds needed: drFeeAmount = 0 and sellerDeposit = 0
795+
796+ const nonce = Date . now ( ) ;
797+
798+ // Seller signs meta tx for commitToBuyerOffer
799+ const { r, s, v, functionName, functionSignature } =
800+ await sellerCoreSDKBuyer . signMetaTxCommitToBuyerOffer ( {
801+ offerId : buyerInitiatedOffer . id ,
802+ sellerParams : { } ,
803+ nonce
804+ } ) ;
805+
806+ // Relayer executes meta tx on behalf of seller
807+ const metaTx = await sellerCoreSDKBuyer . relayMetaTransaction ( {
808+ functionName,
809+ functionSignature,
810+ nonce,
811+ sigR : r ,
812+ sigS : s ,
813+ sigV : v
814+ } ) ;
815+ const metaTxReceipt = await metaTx . wait ( ) ;
816+ expect ( metaTxReceipt . transactionHash ) . toBeTruthy ( ) ;
817+ expect ( BigNumber . from ( metaTxReceipt . effectiveGasPrice ) . gt ( 0 ) ) . toBe ( true ) ;
818+ } ) ;
819+
820+ test ( "non-native exchange token buyer-initiated offer" , async ( ) => {
821+ const exchangeToken = MOCK_ERC20_ADDRESS ;
822+ const drFeeAmount = parseEther ( "0.001" ) ;
823+
824+ // Create a dispute resolver with an ERC20 fee
825+ const { fundedWallet : drFundedWallet } =
826+ await initCoreSDKWithFundedWallet ( sellerWallet ) ;
827+ const drAddress = drFundedWallet . address . toLowerCase ( ) ;
828+ const { disputeResolver } = await createDisputeResolver (
829+ drFundedWallet ,
830+ deployerWallet ,
831+ {
832+ assistant : drAddress ,
833+ admin : drAddress ,
834+ treasury : drAddress ,
835+ metadataUri : "" ,
836+ escalationResponsePeriodInMS :
837+ 90 * MSEC_PER_DAY - 1 * MSEC_PER_SEC ,
838+ fees : [
839+ {
840+ feeAmount : drFeeAmount ,
841+ tokenAddress : exchangeToken ,
842+ tokenName : "ERC20"
843+ }
844+ ] ,
845+ sellerAllowList : [ ]
846+ }
847+ ) ;
848+
849+ // Create fresh buyer/seller wallets
850+ const {
851+ sellerCoreSDK : sellerCoreSDKBuyer ,
852+ buyerCoreSDK : buyerCoreSDKBuyer ,
853+ sellerWallet : sellerFundedWallet ,
854+ buyerWallet : buyerFundedWallet
855+ } = await initSellerAndBuyerSDKs ( sellerWallet ) ;
856+
857+ // Mint ERC20 tokens to fresh wallets (they start with 0 ERC20 balance)
858+ await ensureMintedAndAllowedTokens (
859+ [ buyerFundedWallet , sellerFundedWallet ] ,
860+ undefined ,
861+ false
862+ ) ;
863+
864+ // Buyer creates a buyer-initiated offer with ERC20 exchange token.
865+ // sellerDeposit is 0 to simplify the test setup.
866+ const buyerInitiatedOffer = await createOffer ( buyerCoreSDKBuyer , {
867+ creator : OfferCreator . Buyer ,
868+ quantityAvailable : 1 ,
869+ disputeResolverId : disputeResolver . id ,
870+ exchangeToken,
871+ sellerDeposit : "0"
872+ } ) ;
873+
874+ // Buyer approves ERC20 and deposits offer.price
875+ await approveErc20Token (
876+ buyerCoreSDKBuyer ,
877+ exchangeToken ,
878+ buyerInitiatedOffer . price
879+ ) ;
880+ const buyerDepositTx = await buyerCoreSDKBuyer . depositFunds (
881+ buyerInitiatedOffer . buyerId ,
882+ buyerInitiatedOffer . price ,
883+ exchangeToken
884+ ) ;
885+ await buyerCoreSDKBuyer . waitForGraphNodeIndexing ( buyerDepositTx ) ;
886+
887+ // Seller creates a seller account
888+ const seller = await createSeller (
889+ sellerCoreSDKBuyer ,
890+ sellerFundedWallet . address
891+ ) ;
892+
893+ // Seller approves ERC20 and deposits drFeeAmount into the protocol
894+ await approveErc20Token ( sellerCoreSDKBuyer , exchangeToken , drFeeAmount ) ;
895+ const sellerDepositTx = await sellerCoreSDKBuyer . depositFunds (
896+ seller . id ,
897+ drFeeAmount ,
898+ exchangeToken
899+ ) ;
900+ await sellerCoreSDKBuyer . waitForGraphNodeIndexing ( sellerDepositTx ) ;
901+
902+ const nonce = Date . now ( ) ;
903+
904+ // Seller signs meta tx for commitToBuyerOffer
905+ const { r, s, v, functionName, functionSignature } =
906+ await sellerCoreSDKBuyer . signMetaTxCommitToBuyerOffer ( {
907+ offerId : buyerInitiatedOffer . id ,
908+ sellerParams : { } ,
909+ nonce
910+ } ) ;
911+
912+ // Relayer executes meta tx on behalf of seller
913+ const metaTx = await sellerCoreSDKBuyer . relayMetaTransaction ( {
914+ functionName,
915+ functionSignature,
916+ nonce,
917+ sigR : r ,
918+ sigS : s ,
919+ sigV : v
920+ } ) ;
921+ const metaTxReceipt = await metaTx . wait ( ) ;
922+ expect ( metaTxReceipt . transactionHash ) . toBeTruthy ( ) ;
923+ expect ( BigNumber . from ( metaTxReceipt . effectiveGasPrice ) . gt ( 0 ) ) . toBe ( true ) ;
924+ } ) ;
925+ } ) ;
926+
727927 describe ( "#signMetaTxRedeemVoucher()" , ( ) => {
728928 test ( "non-native exchange token offer" , async ( ) => {
729929 const commitTx = await buyerCoreSDK . commitToOffer ( offerToCommit . id ) ;
0 commit comments