wyvernprotocol / wyvern-v3

Wyvern Protocol v3.1, Ethereum implementation
https://wyvernprotocol.com
MIT License
298 stars 121 forks source link

Examples to integrate with Wyvern from a smart contract #55

Open aramalipoor opened 3 years ago

aramalipoor commented 3 years ago

Hello there,

Thanks for the amazing platform! We're building a NFT platform with some special rules, because of these (specifically that "transferFrom" and "approve" cannot be called by owners themselves) we have to automatically list them on OpenSea.

From my understanding so far we need to call approveOrder_ from a contract who has the ability to do approve and transferFrom on those NFTs. We were thinking of having a "Marketer" contract which has a special access to do these operations on all NFTs on behalf of holders.

Assuming, above is correct there are a few questions I have:

  1. Do we need to have a proxy for this Marketer contract of ours? e.g. to call "registerProxy" on its constructor. If yes is it possible to implement required capabilities within our Marketer contract so we can avoid having a proxy?
  2. These orders need to be using ETH (and WETH maybe?) in terms of being asymmetrical, is it possible to do what we want to achieve? i.e. To put the NFT for sale on automatically behalf of holder for a certain price, and have it automatically show up on OpenSea?
  3. Is there any "solidity" examples available where a smart contract calls Wyvern exchange, there are some javascript examples but mapping them to solidity is a bit tricky.

Thank you so much in advance :)

aramalipoor commented 3 years ago

For a PoC so far this is what I ended up with, but currently have 2 problems:

  1. How to generate replacementPattern?
  2. Should this operation automatically list the sale order on OpenSea? If so I don't see it on testnets.opensea at least.

    
    bytes constant emptyBytes = bytes("");
    bytes constant replacementPattern = bytes(hex"000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000000000000000000000000000000000000000000000000000");
    
    function putForSale(
        address owner,
        uint256 tokenId,
        uint256 salePrice
    ) public virtual isDirectory()
    {
        uint zero = 0;
        uint one = 1;
    
        address zeroAddr = address(0);
        bytes memory transferCalldata = abi.encodeWithSignature("transferFrom(address,address,uint256)", owner, zeroAddr, tokenId);
        address[7] memory addrs;
        uint[9] memory uints;
    
        {
            addrs = [
            // Exchange
            address(_wyvernExchange),
            // Maker
            address(this),
            // Taker
            zeroAddr,
            // Fee Recipient
            _openSeaWallet,
            // Target (Asset Contract)
            address(_directory),
            // Static Target
            zeroAddr,
            // Token used to pay for the order, or the zero-address as a sentinel value for Ether
            zeroAddr
            ];
        }
        {
            uints = [
            // Maker Relayer Fee
            500,
            // Taker Relayer Fee
            zero,
            // Maker Protocol Fee
            zero,
            // Taker Protocol Fee
            zero,
            // Base price of the order (in paymentTokens)
            salePrice,
            // Auction extra parameter - minimum bid increment for English auctions, starting/ending price difference
            zero,
            // Expiration timestamp - 0 for no expiry
            block.timestamp,
            zero,
            // Order salt, used to prevent duplicate hashes
            uint(keccak256(abi.encodePacked(block.difficulty, block.timestamp)))
            ];
        }
    
        {
            _approveWyvernOrder(
                addrs,
                uints,
                transferCalldata
            );
        }
    }
    
    function _approveWyvernOrder(
        address[7] memory addrs,
        uint[9] memory uints,
        bytes memory transferCalldata
    ) internal {
        _wyvernExchange.approveOrder_(
        addrs,
        uints,
        // Fee Method (split fee): Maker fees are deducted from the token amount that the maker receives. Taker fees are extra tokens that must be paid by the taker.
        WyvernExchangeInterface.FeeMethod.SplitFee,
        // Side (sell)
        WyvernExchangeInterface.Side.Sell,
        // Sale Kind (fixed price)
        WyvernExchangeInterface.SaleKind.FixedPrice,
        // How to call (call)
        WyvernExchangeInterface.HowToCall.Call,
        // Call Data
        transferCalldata,
        // Replacement Pattern
        replacementPattern,
        // staticExtradata
        emptyBytes,
        // orderbookInclusionDesired
        true
        );
    }
aramalipoor commented 3 years ago

This is an example NFT where I called approveOrder_ but don't see it listed on OpenSea: https://testnets.opensea.io/assets/0xda25087323285AE25e00E8C373a9184fb558D831/100 on this transaction https://rinkeby.etherscan.io/tx/0xe32bf43e6a08e9fa0e28c54e55db68d0d2849d0c93e3d26acb81ab880cabfc47#eventlog

aramalipoor commented 3 years ago

Now I figured out I have to call OpenSea API to "post" the order...

There's a problem which I'm guessing is on OpenSea backend side. I'm using a different "maker" than the "owner" in the calldata...

So currently, I have marker = Our Marketer Contract, and owner = owner of tokenId, and I'm getting this error from OpenSea backend:

{
    "success": false,
    "errors": [
        "[\"Transfer calldata doesn't match.\"]"
    ]
}

And I think this is because OpenSea backend expects "owner == marketer" which is just one way of working with Wyvern protocol if I'm not mistaken...

For more context, calldata is for transferFrom(owner, null_address, tokenId), but the maker is our Marketer smart contract, so they are different. If I set maker = "owner" the contract fails due to maker != sender, but if set owner = "marker" then the contract fails because the Marketer (maker) is not the actual owner of tokenId...

curl --location --request POST 'https://rinkeby-api.opensea.io/wyvern/v1/orders/post/' --header 'Content-Type: application/json' --data-raw '{
    "exchange": "0x5206e78b21ce315ce284fb24cf05e0585a93b1d9",
    "maker": "0x3f539db004cb22c96d69d0e3eb8974d46ed93254",
    "taker": "0x0000000000000000000000000000000000000000",
    "makerRelayerFee": "250",
    "takerRelayerFee": "0",
    "makerProtocolFee": "0",
    "takerProtocolFee": "0",
    "makerReferrerFee": "0",
    "feeMethod": 1,
    "feeRecipient": "0x5b3256965e7c3cf26e11fcaf296dfc8807c01073",
    "side": 1,
    "saleKind": 0,
    "target": "0xda25087323285ae25e00e8c373a9184fb558d831",
    "howToCall": 0,
    "calldata": "0x23b872dd000000000000000000000000431922e8ede1ce07ed80c89ac4c302e23dc6ba2000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000064",
    "replacementPattern": "0x000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000",
    "staticTarget": "0x0000000000000000000000000000000000000000",
    "staticExtradata": "0x",
    "paymentToken": "0x0000000000000000000000000000000000000000",
    "quantity": "1",
    "basePrice": "550000000000000000",
    "extra": "0",
    "listingTime": "1624714038",
    "expirationTime": "0",
    "salt": "108978690099335258361352357558901730935341035577208231443103633475927170030228",
    "metadata": {
        "asset": {
            "id": "100",
            "address": "0xda25087323285ae25e00e8c373a9184fb558d831"
        },
        "schema": "ERC721"
    },
    "hash": "0xb48cd508ec5caab9ab9b4fcb4d83afeeb53b009dea3393ca9a167158bb38d1fe"
}'
aramalipoor commented 3 years ago

@cwgoes @MarvinJanssen would you be able to help me on above?

Rotemy commented 3 years ago

@aramalipoor Hey Aram, we are also trying to initiate a sell and buy orders via smart contract. Have you manage to make any progress?

priviprotocol commented 2 years ago

Hi @Rotemy @aramalipoor -- we are facing a similar issue Were you able to find a solution?

Rotemy commented 2 years ago

Yes, but the seller or buyer should either be the smart contract or the user has to sign the sell/buy order in advanced

priviprotocol commented 2 years ago

Hi @Rotemy,

Thanks a lot for your reply.

Then, this would be correct?

bytes memory transferCalldata = abi.encodeWithSignature("transferFrom(address,address,uint256)", address(this), zeroAddr, tokenId);

where address(this) would be the contract address and then the buyer.

Rotemy commented 2 years ago

well, there is a replacement pattern that will change the address(0) to the buyer, so you can keep it like that or change zeroAddres to buyer address.

On Fri, Nov 19, 2021 at 2:24 PM priviprotocol @.***> wrote:

Hi Rotemy,

Thanks a lot for your reply.

Then, this would be correct? bytes memory transferCalldata = abi.encodeWithSignature("transferFrom(address,address,uint256)", address(this), zeroAddr, tokenId);`

where address(this) would be the contract address and then the buyer.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/wyvernprotocol/wyvern-v3/issues/55#issuecomment-974027514, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAFOG75E5AWCAUIIK6WPQRLUMY6YVANCNFSM47HLK4KQ .

-- Kind regards,

Rotem Yakir +972547545695

priviprotocol commented 2 years ago

Thanks again for the answer @Rotemy. Let me state better our problem. We are trying to list a selling offer on Opensea from our contract.

This is the post request we are doing:

{
    "exchange": "0x5206e78b21ce315ce284fb24cf05e0585a93b1d9",
    "maker": "0x568A0f32BC48f278fe3C822E9992dD478598b3B1",
    "taker": "0x0000000000000000000000000000000000000000",
    "makerRelayerFee": "250",
    "takerRelayerFee": "0",
    "makerProtocolFee": "0",
    "takerProtocolFee": "0",
    "makerReferrerFee": "0",
    "feeMethod": 0,
    "feeRecipient": "0x5b3256965e7c3cf26e11fcaf296dfc8807c01073",
    "side": 1,
    "saleKind": 0,
    "target": "0x8385c95eb77ceab24f8bb05b33dc7602840c0ba1",
    "howToCall": 0,
    "calldata": "23B872DD000000000000000000000000568A0F32BC48F278FE3C822E9992DD478598B3B100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003",
    "replacementPattern": "000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000000000000000000000000000000000000000000000000000",
    "staticTarget": "0x0000000000000000000000000000000000000000",
    "staticExtradata": "0x",
    "paymentToken": "0x0000000000000000000000000000000000000000",
    "quantity": "1",
    "basePrice": "100000000000000000000",
    "extra": "0",
    "listingTime": "1637328412",
    "expirationTime": "0",
    "salt": "70178943407415076223843289749154878162928002278408319126229368582024804639243",
    "metadata": {
        "asset": {
            "id": "3",
            "address": "0x8385c95eb77ceab24f8bb05b33dc7602840c0ba1"
        },
        "schema": "ERC721"
    },
    "hash": "0xced35f245951d3622eeee38066d3a71ab1218e7fcc7d451d213d133d5d0dcef1"
}

after calling approveOrder on Wyvern Exchange https://rinkeby.etherscan.io/tx/0x2153ca6f7cc1aac8f08e7a65df424bab1b2d4b3209dccd9d9a6f0c4885cb6602#eventlog

However the response we are getting is:

"errors": [ "['You must approve assets #3 for trading before creating an order']"

I would be great if you could give some guidance or support on that. Thanks in advance.

atsmsmr commented 2 years ago

Hi @Rotemy, @priviprotocol, I am also trying to initiate sell order via smart contract. I tested approveOrder_() based on aramalipoor's code above, but it did not work. Did you achieve that in a completely different way than aramalipoor? Could you give me some advice?

Ghijo1 commented 2 years ago

Hello guys, do you know how to get the orderbook of a token from the wyvern exchange?

qaiszero commented 2 years ago

Hello guys, do you know how to get the orderbook of a token from the wyvern exchange?

you mean how to get order data for available atomic matches on Wyvern? I want to know the same