0age is the Head of Protocol Development at OpenSea. This is a conversation about Seaport, the new marketplace protocol for buying and selling NFTs.
0age takes us through a tour of the Seaport protocol, talking about how it's architected; how conduits and zones work; and we even get into the low level gas optimization work they've done on the contracts. I hope this can be a helpful resource for anyone looking to understand the Seaport protocol or anyone who's building with NFTs more broadly. I also consider 0age to be a true veteran of the space, and hearing him talk through the design of the protocol can be an educational experience in its own rights.
1:42 why build Seaport
10:20 the Seaport architecture
12:44 EIP712 signatures
14:17 the global concept of a nonce
16:02 EIP1271 and bulk listings
17:18 the Executor and conduits
25:08 zones, additional rules that can be applied on top of an order
29:47 implementing English auctions via zones
32:17 layers of the stack
40:42 gas optimizations and understanding the low-level behavior of the EVM
58:40 the interaction between OpenSea the product and Seaport the protocol
01:07:06 criteria based items, and partial fills
01:17:50 ideas to build on top of Seaport
[00:00:18] Sina: Hey everyone. Welcome to another episode of Into the Bytecode. Today, I sat down with my friend 0age. 0age is the Head of Protocol Development at OpenSea. And this was a conversation about Seaport, the new marketplace protocol they've developed for buying and selling NFTs.
Seaport was initially developed by OpenSea, as a base layer for their marketplace. But it's now an open project that others are starting to contribute to and build on. Now, before jumping in, I want to give a heads up that this was a technical conversation. Definitely more so than previous conversations we've had on this podcast. 0age takes us through a tour of the Seaport protocol, talking about how it's architected, how conduits and zones work - we even get into a bit of the low level gas optimization work they've done on the contracts.
So. I hope this can be a helpful resource for anyone who’s looking to understand the Seaport protocol or anyone who's building with NFTs more broadly. I also consider 0age to be a true veteran of the space, and hearing him talk through the design of the protocol can be an educational experience in its own right.
I definitely personally found that to be the case.
And so with that, I hope you enjoy the conversation.
[00:01:40] 0age: we could help set up a more robust standard for a way of doing this.
That's not just specific to open seat that everyone could benefit from because it does seem like there's, there's a proliferation now of interest in creating marketplaces and, um, and designing more tailored experiences. And, um, having, having something that everyone could use really, um, was an exciting proposition that we use as well.
So that's, that's when, once that was all squared away and we were fully on the new version of Wyvern that's when development efforts really started. we began with making sure that it was efficient and sound from an architectural perspective. And after performing a security review on that and getting the full feature set that we felt Seaport should have all ironed out.
Then we did a second pass that is geared around optimization,
[00:02:53] Sina: Right. And that's where all the assembly comes in.
[00:02:58] 0age: And then, Dylan Keller and transmissions, really just, we, we descended on these contracts and, um, and that's what a lot of the discussion and, um, narrative has been around Seaport as well. What the hell is happening here?
There's a lot of assembly code, but, um, but that I think is also when you consider the sheer scale. That the --
[00:03:26] Sina: you’re saving like hundreds of millions of dollars worth of gas or, or something crazy like that.
[00:03:31] 0age: Yeah. If there is any, if there's any contract where it's justified, this seems like it would be it and will have important knock on effects for the entire ecosystem
[00:03:43] Sina: Yeah.
[00:03:43] 0age: with gas congestion. but it underscores the importance of review, which is why we're running this CodeArena competition with a million dollar price for it, now through Friday, the 3rd of June. So highly encourage everybody to go and check that out and submit findings. Um, we are, we're very excited about that. Yeah. after that wraps it's really just review. From as many, many independent parties as we can get our hands on and feedback from the broader community, as well as educating them on how it works and what the architecture is like and, and how there's different mental models for dealing with Seaport than there are with some of these other NFT Marketplaces.
[00:04:35] Sina: So when you're talking about, when you set out to design this, who are you designing this for?
[00:04:37] 0age: It’s designed for everybody. The question of buying and selling and more generally moving NFTs around is pretty fundamental. And, rather than forcing everyone to roll their own solution, which if, if someone gets compromised, that's bad for everyone that's that has trickled through effects to, to the whole ecosystem if there were be an incident and on top of that, there's, there's a real benefit to if, if we as a community and an ecosystem can all agree that it makes sense to be using a shared source of where the listings are coming from, being discovered from. Then there's so many efficiency gains to be had from that where you have a much more flexible and composable way of doing this that's not siloed by the particular, place where you want to list or --
[00:05:51] Sina: I mean, it's kind of the whole premise of crypto, right. Is to have a shared database and then potentially different views and applications built on top of that. Right?
[00:06:00] 0age: precisely. Yeah. And, and it's, it's very much, uh, we're still in the earliest stages of this and it, it remains to be seen to what extent everyone's going to lean into that. But my hope personally is that we'll, all coalesce around something like Seaport to save everybody lots of gas and to make it easier to build with a sort of shared set of primitives that are flexible enough, that they can encompass all the various use cases while also being opinionated enough, that from the perspective of users that just want to buy and sell them NFTs, that they have an interface they're familiar with that you're not having to reapprove your tokens all over the place.
[00:06:55] Sina: Right. That's huge.
[00:06:56] 0age: Yeah.
[00:06:57] Sina: And in some ways that makes me think of, I mean, this, uh, protocol, product relationship is a very interesting one. and, I think what's been particularly cool in this case is that you have a long history of data on how people are engaging with the NFT ecosystem at a product level.
Right. And then you can design a protocol that caters to that and. It's I mean, I'm familiar with this, with this discussion more in the realm of blockchains and like L1s and L2s where you, you design an L1 protocol So that everyone else can get benefit from it. Um, but then, you know, there is potentially something to be said of like, what if you designed the protocol for a more specific use case, are there gains to be had there, because you can make specific trade-offs that are useful there.
And I mean, I definitely think that NFTs as these like digitally native items that, you know, have the concept of ownership embedded in them is like such a fundamental primitive of this whole new world that we're building, where having a truly decentralized protocol that's flexible that everyone can contribute to and benefit from makes a ton of sense.
[00:08:17] 0age: Yeah. Yeah. One other big benefit of if we can all work together to build a really rock solid primitive, that is flexible enough for all these different use cases, then there's nothing stopping us from deploying it across every EVM compatible too,
[00:08:35] Sina: Yeah.
[00:08:36] 0age: because you're absolutely right. That there are going to be L1 and mainnet are I think that there's a really strong case to be made for NFTs being that their origination point being mainnet. Provenance is really a critical aspect of NFTs and that sense of permanence. And that really gives them the sort of bedrock that a lot of NFT ecosystem we're looking for. But then on the flip side, you have users that really just want to be using these NFTs and especially,
[00:09:18] Sina: which will happen on L2s.
[00:09:20] 0age: yeah, and if there's a protocol that can reside on all of these environments, it's still going to be really important to have a good settlement layer that's hyper-efficient on L1, because at the end of the day, your Bored Ape is on L1 and that's where you're going to want to buy and sell.
But at the same time, you've got some cool games that you are transacting all the time with your NFT or sign-on methods. Um, I mean the, the, the, the applications of NFTs are an entirely different discussion and very excited, but having the same protocol that can live in both environments.
And it's, I think that is going to make building on different environments, different L2s, way more of a tractable thing for new entrants.
[00:10:18] Sina: Yeah. well, I I definitely want to dive into the Seaport architecture,
[00:10:23] 0age: Yeah. I I'm very much, uh, excited to talk about the architecture. So. At a high level. First thing to sort of get across -- Seaport is a single contract. So the marketplace itself, there's a lot of libraries that are all being used and the inheritance tree is pretty crazy. But it's all a single contract and it was not easy to get it to that point.
But the idea there is that now you can really leverage in-memory operations. So if you have an architecture that by default is making all of these external calls and has this modular architecture as part of its core. Then that's a lot of overhead. Every call is 2,600 gas first time you touch an account and then you got to do various S loans.
And what have you, especially if you want those modular pieces to be upgraded, that really adds up quickly. There's also, you're embedding assumptions about the way things are going to work by the way you lay out those modules. A good example would be if you have a module that's geared around calculating what the creator payouts are going to be.
Well, now, what if I want to trade NFTs for NFTs right? there, all of a sudden, some of these modules I have to break down and it's that the questions are even necessary. The way that Seaport works, single contract. Everything happens in memory, meaning I'm an offerer -- I want to create a new offer laying out exactly what items I'm willing to spend. Exactly what items I, or others need to get back as part of that. And now we can, as the fulfiller that's coming along and says hello, I would like to take out a listing. We can operate entirely in memory and basically spend every offer item and credit every consideration. To give a little more color on the architecture within that, that high-level context at the bottom root level, you have a lot of functionality around enforcing EIP712, which is the sort of base building block of when you want a signed order where every order has the exact same structure and syntax that it is when you sign it, which is a great, powerful concept honestly. You're going to get a pop up that actually lists out each offer or item that you're willing to spend. And you can see it right there. We're also, you can create a, uh, metamask snap that will actually show the NFTs that are going to be leaving your wallet. If it happens and show the NFTs that you're going to get back or the, the, um, tokens you're gonna get back and will list everything out and then once you, as the user get to choose exactly what matters to you in terms of what you're spending, what you get back, but then other users, the fulfillers have a whole range of choices as to how they're actually going to fulfill those orders. As long as, as long as all of the conditions were meant that were set out originally.
So base layer is the rules around that -- constructing type hatches and ensuring that the domain is properly applied. All of that. you go up a little further. There's a global concept of the nonce. Now a nonce is not incremented every time that an order gets fulfilled, like you would with a transaction on Ethereum. A nonce is - any offer. Any user can call increment nonce at any point and increment their nonce. a lot of users will just leave it at zero, but if you want to bulk cancel all of your listings at once you increment your nonce and that's a component of the payload So from there you get into logic around performing various assertions, um, retrieving information about orders, verifying signatures, signature verification. There's a few different methods that you can, if you want to create an order, sign an order, and list your NFT, you can always do it directly.
So you can call validate and list on chain, is actually a feature of Wyvern as well. It's not well understood, but, and it's, but what this effectively does is it says, I'm happy to list this. Don't worry about a signature, it's coming from the origin, but you can also sign ECDSA payload. Just based on the parameters of all the offer items, consideration items, so on and so forth, including, um, 2098.
So you can give a compressed signature that’s in 64 bytes, which saves us a surprising amount of gas because you lop off a whole order that way. and then finally, and this is a, I think a really exciting thing that this design space has not been explored. And I'm really excited to see what the, community comes up with around this is 1271 for signing orders, So like one really compelling use case for this is bulk listings. Rather than just signing a particular listing and say, okay, I'm happy to sell this NFT. I can create 10 or a hundred,
[00:16:36] Sina: whatever bundle that you want.
[00:16:37] 0age: any, any size that I want, different parameters, prices, expirations, whatever, as long as I'm doing it from a smart wallet. So this is definitely more of a power user feature.
You'd need to, to pipe things through your wallet, but now I sign a Merkle root containing all of those words. And then for the signature, I actually supply a proof that the particular order hash is in that Merkel root. So that's just, it's honestly, just one example. There's a number of very cool things you can do with 1271, um, continuing up, you then get into the exactly.
So the executor is, or executer executor,
[00:17:26] Sina: Yeah.
[00:17:27] 0age: is you're effectively going to process through a list of executions or token transfers that are specified by, by the fulfiller that's calling in and says, here's how I want – the orders I want to fulfill. And then it will, it will go through. Fire off all of the token transfers either directly. You can always use Seaport directly, give it your token approvals and use it, um, in an isolated context, but you can also use what are called conduits. So this is how we solve for this, whole area of upgradeability and extensibility of protocol.
Nobody wants to build on a protocol where the rules could just change out arbitrarily underneath you. Nobody wants to build on a protocol where they're not in control of the destiny and, um, and security of their user base. So -- anybody can deploy a conduit and a conduit is really just a place where users can approve tokens.
And then Seaport or other marketplaces can instruct the conduit. They tell the conduit to transfer tokens. And the conduit then has an owner. Seaport itself is unowned. unupgradable, hyper structure, right? Conduits have an owner - a DAO, a multi-sig -- whatever makes sense for, for your, um, trust model and what you as a user prefer.
That's a whole debate that what, what makes sense from upgradeability perspective, some would bias toward absolute immutability, or maybe just being able to -- you cede a conduit by giving it a particular marketplace that can tell it what to do, but maybe you have a set of guardians that can remove it if there's a problem. Sort of like a kill switch. Um, then on the other side of, of the equation, you have, you have a whole cohort of users that they actually want a trusted entity or source to ensure that they're staying up to date. Give one token approval. And now as this quickly evolving space matures and the ecosystem adapts and evolves that they don't have to constantly be reapproving, unapproving tokens and it all just kind of works.
Particularly an ideal steward of a conduit would be someone who really has their ear to the ground is staying very on top of various developments ensuring that there's no problems with bugs, with the contracts, they can call that conduit. And, um,
[00:20:40] Sina: So, so the conduit and sorry to be jumping around a bit. So the user hands a signed bundle of offers to the conduit. And the conduit is responsible for kind of scouring the marketplace, uh, maybe kind of running its own front end.
[00:21:02] 0age: so, so the way that it works is that when you, when you're creating orders, you can optionally specify a conduit. You actually specify a conduit key. This is a pretty cool trick. I'll just, we'll geek out for a second. So generally when you, when you want to call a contract, You need to ensure that there is indeed a contract at that address.
Because if I call what I think is a contract and it's actually an ELA and just a regular account with no code, I'll get back a success status. Like I want to transfer you some, Ether, I put some data like a message. Okay. That works success. Great. If I thought that was a contract and there was going to be some logic that was executed there, and I'm just going off of that success, then that's a problem. If I thought that was a token and I tell it, transfer this from you to me. And I send ---
[00:21:58] Sina: so this is where the magic number comes in.
[00:22:00] 0age: Exactly. Yeah. So what solidity and Vyper do is generally they will actually perform a [inaudible] they'll say, what, what contract code is. Is there. And if there's none, then they'll revert. If you try a high-level, um, contract call. Now what Seaport does is says, we can actually skip this entire check.
We need to know two things. First. We need to know that there's a contract. Two, We need to know that contract will faithfully execute the token transfers. That's very important. If we could just, you could just specify an arbitrary conduit. I can do it as an attacker. I could deploy my own conduit, implement the interface, You call it and say, okay, transfer tokens from Sina to 0age.
[00:22:58] Sina: does whatever.
[00:22:59] 0age: cool. I did it. And it did it. So now you can't compose with comments. So they, all the conduits are employed from a known, trusted factory. So they'll all have the exact same implementation. If a conduit is deployed, then we know that it's going to faithfully transfer tokens or it's going to revert, right?
If, if the color
[00:23:20] Sina: so it does some sort of a check before and after and make sure that there’s conservation of energy of sorts.
[00:23:28] 0age: We give the conduit key, which is actually the salt that's used to deploy the conduit via [inaudible].
[00:23:36] Sina: Uh huh.
[00:23:37] 0age: That means that once we, we derive that, that counterfactual,
[00:23:43] Sina: you know what code is going to be in there.
[00:23:44] 0age: the only code that can exist, there is the correct code from the condom. That's deployed from a deployer, or it hasn't been deployed yet.
In which case we perform the call and we ensure that we got returned back because if I call it an EOA, you don't get return data back. So this lets us skip the [inaudible], shave off a chunk of gas, which is great. And, um, anyway, that's, that's the idea there,
[00:24:15] Sina: Yeah,
[00:24:15] 0age: which I think is a good pattern. And I encourage any developers to think through, especially in cases where you have contracts, that, that you need to know that there's a contract there -- return some data, even if it seems superfluous because the caller can use that to their advantage, to, to, as an efficiency gain.
[00:24:36] Sina: right.
[00:24:37] 0age: And it's just an added assurance that the call worked as intended. I think that's a really good pattern in general.
[00:24:42] Sina: Yeah.
[00:24:43] 0age: so yeah, what the executor does is it will, it will aggravate calls to get in conduit as they come. If you want to transfer tokens, we check and see if they all have the same conduit. And then once, once we're all done, or if a new conduit is, is encountered.
Then we send off all the transfers to the first conduit as part of a batch. we should talk about zones next
[00:25:09] Sina: Yeah,
[00:25:10] 0age: the next thing that comes in. So the zones the best way to think about zones.
In their current format is that they give a thumbs up or thumbs down on if in order is valid, like an additional rule set that you can apply on top of given order. So you have the choice as an offerer. Do I want to use a restricted order and a restricted order can only be fulfilled on the other side, if the zone you give it a call and says, is this a valid order? You have all the order parameters. It also has context on the other orders that were previously included as part of that atomic bundle.
[00:25:57] Sina: And an order at this point includes both what was offered and the other side of the market. Right. it's a complete bundle that will execute.
[00:26:05] 0age: right, all the offer items, all the consideration items, the particular offerer, all of that. So some simple things that you would see happen in the zones, like one really basic type of zone that will almost certainly be using right out of the gates at Opensea is a global [inaudible]. Something that literally like has the whole system been paused? then don't let this go through.
And if not --
[00:26:33] Sina: Wait a global what?
[00:26:39] 0age: another is, remember we talked about nonces before that at a very low level. And every offer, every user has their own nonce that they can just cancel everything. Well, what if I don’t want to cancel everything. I want to cancel all of my items, all my listings, just in this particular collection. That collection can maintain their own zone.
And I think this is a really powerful thing. This is like kind of slept on by a lot of, lot of people right now as they’re reviewing Seaport. Zones give collections the actual creators, a way to, to have their own rule sets around their own markets to help keep their community safe, which they're close to their own community to add additional functionality and more advanced types of orders based on something that's specific to their own selection, particularly cool with on-chain NFTs.
'cause you can use those on chain properties to help dictate -- Like if you have an NFT that has dynamic metadata. Now I can create orders that are predicated on that dynamic medadata, but that collection, those creators, they could have a [inaudible] nonce, you could have a nonsense, like the creator could increment it and reset the marketplace for instance,
[00:28:21] Sina: Right. The creator is running some web application that is generating these offers for the users and they can, they can in a sophisticated -- more sophisticated way, increment the nonces and validate historic offers or bake in other sorts of requirements --
[00:28:39] 0age: you can implement, you can implement blacklists if items are stolen or compromised --
[00:28:45] Sina: right.
[00:28:46] 0age: or you have bad actors that are, are trying
[00:28:50] Sina: And this all happens without, um, it's really just tied to an offer like that same user could opt out of this whole system and just generate a, create an offer that doesn't include this --
[00:29:02] 0age: 100%. Yeah. The offer chooses what zone they want on there, or what, what conduit they want on their order. The best way to think about conduit is it's like, this is what the offerer that user that's creating these listings is electing to -- it's their decision on where, what trust model they want to wear, how they want to handle their token approvals.
So a user would probably be using the same conduit across all of their different --. Uh, then zones the best way to think about that is that that's what the user is choosing in the context of this particular type of order, maybe collection specific, maybe like English auctions and, uh, interesting like reverse Dutch Auction type stuff.
It's got a non-linear, non linear function in terms of how the price curve goes. Those would all tap into they'd have a specific zone that, um, you --
[00:30:08] Sina: Wait. So how, how would you implement an English auction via zones?
[00:30:12] 0age: so there's a number of ways that, that you might tap into the zone for something like this, depending on, on how you want to run the auction. I think the big things with English auctions is there's, there's generally a reserved price.
[00:30:27] Sina: Right --
[00:30:28] 0age: And the reserve price, depending on how you, you usually don't want bidder to know what the reserve price is.
So one of the things you can do with zones to help facilitate English auctions as well as if you're concerned about frontrunning in general, this is another direction you can take is you can implement commit reveal on your zone. So you, as a bidder, as a fulfiller of this particular restricted order that’s specifying a commit reveal zone, you have to, five minutes ahead of time, you have to sign a commitment that is still blinded. It, no one knows exactly what you're committing to, but then you reveal it and you demonstrate, yeah, I wanted to buy this five minutes ago. I still want to buy it. And then someone cant step in and frontrun you or as an English option case, you'd commit to the reserve price.
[00:31:26] Sina: Right.
[00:31:27] 0age: Yeah.
[00:31:29] Sina: Ah, super interesting.
[00:31:30] 0age: Yeah. Zone's, I'm telling you, we're just scratching the surface, the pieces here. You don't have to use zones at all. If you don't want that modularity, you don't want that extra call because as we sort of talked about earlier, Seaport is designed that it could just be one contract that if one cared about is doing things as cheaply as possible.no need to set a zone call.
[00:31:53] Sina: Right.
[00:31:54] 0age: last thing is zoned can do that is worth mentioning. Is that a zone can cancel that order. So if you set a zone, it's another way of thinking about it is that the zone it's giving a thumbs up thumbs down, right? But it can give us an explicit thumbs down and cancel order.
Now it's easy enough to create a zone that doesn't --
[00:32:17] Sina: So wait, so before shifting gears. So just to, just to maybe retrace the layers of the stack, right? So at the very bottom, there is like signature kind of checking logic, right? then on top of that, there's this concept of a nonce, which, uh, is set by a user who's creating an offer and these nonces --
[00:32:45] 0age: that lets you bulk cancel by incrementing that nonce
[00:32:49] Sina: Okay.
[00:32:49] 0age: at their leisure.
[00:32:51] Sina: As they want. And then the user has this ability to create an offer and they can, um, they can sign multiple offers at the same time using this EIP 1271, bulk listings and each offer will both point to a particular conduit, uh, optionally, or just like the, the top level Seaport contract as it's conduit.
And I imagine that each offer will also specify a zone optionally that it, that it kind of complies to, um -- Then these offers are handed over to this executer contract, which
[00:33:42] 0age: This, this contract is basically just, you can think of it like middleware, right? All of the tokens remain custodied by the offerer, and it's only the, the offerer gives, gives, uh, an order that says, as long as these very specific conditions are upheld,
[00:34:04] 0age: that are -
[00:34:05] Sina: have the permissions to do the transfer --
[00:34:07] 0age: then you, then we can move the tokens directly to the fulfiller
[00:34:14] Sina: That makes sense.
[00:34:15] 0age: which is I think a very, very important distinction make that this is entirely a system where offerers choose the terms that matter to them and, and under what conditions they would be willing to spend items and let them leave their wallet. Mainly it's generally that they're getting something back. Fulfillers that have full choice as to which of those they're --
[00:34:47] Sina: Right. It's really only signatures that are changing hands and permissions. Like an offer has given a particular kind of conduit permissions to do a particular transfer, right. So these, these conduits come back with a list of. Uh, potentially successful trades to happen and then at some point here, the zones also come in as a final check.
Uh, So I don't know if it would be the conduit that's made doing that check or it's the executor before doing all the transfers? and then the system -
[00:35:22] 0age: yeah, this we've sort of begun at, at the base layer of -- all right. You need to be able to sign. And construct and sign a valid listing. Right? And then we've also talked about the very last step, which is okay. We now have this set of executions. Let's go and tell the correct conduit in one bulk, uh, set of, of transfers.
Like here's what you got to do and the conduit will do it And that leaves this whole middle ground now. All right. Well, what about from the fulfillers perspective? So we've created these warmups.
[00:36:07] Sina: Yeah.
[00:36:07] 0age: We know, we know how the orders will be executed, but how do we get from A to Z here? And that's where we get into all the different methods for fulfillment.
So a fulfiller they have a number of a range of choices, depending on the type of order and how many orders and all of that. In the most simple case, which I think we should start with, is in the order fulfiller is the library that is using simplest case is just call fulfill order. When you call the fill order, you're actually creating a second order, an implied mirror order of the one that you're filled, where every item that's being offered on my second implied listing, or order, I'm just going to say, I'll take all of those offer items and set them as consideration items with me as the recipient. Then on the consideration side, I guess look at all the consideration items and my implied order. I'm going to create an offer and pay out each of those, myself. So this, this is the most basic standard way that you would fulfill a specific order. You discover that order, you call [inaudible]
there's a few important downsides to doing it that way if there are other routes available. One is that consider the case where you are making an offer to purchase an NFT. So you own a cool cat. All right. I put out an offer on your cool cat to buy it for 10 WETH. you want to accept that. So you call fulfill order, you now create a mirror that, and you can also specify a conduct as the fulfiller as well. You have that full flexibility, but you create a mirror order. Now that says, all right, you are offering to give me 10weth. your consideration items are going to be this cool cat goes to 0age, call it 0.25 weth goes to opensea and 0.1 weth goes to cool cat creator. So now, what would happen practically here is you create the mirror order of that and you now are offering your cool cat.
You're also offering weth to open sea and offering weth to the cool cats. And then you get back weth as well. That means you have to have weth approved as well. And that there's a possibility of redundant transfers.
So there's another method that's called and also has a lot more call data than you necessarily need in these simple cases, like the hot paths, which are effectively just buying and selling a single NFT and have ETH and ERC 20, basically the fallback case of what is the standard behavior on other NFT marketplaces.
Right? And that is still a very common use case. So for these there's this method of order fulfillment called basic fulfillment, or you call fulfill basic order. What that lets you do. You pass in a subset of parameters. That is, it cuts down on call data. We can do a number of tricks that where we don't need to validate quite as much information about what's happening and we can transfer it, like in this accept offer case.
We can just source those, that weth directly from you, the original offer, and you pay out to your own consideration items and then just give the remainder to meet a fulfiller who's giving you the NFT, if that makes sense. So that cuts down on some additional overhead, but the fill basic order method is pretty cool as if you're reviewing the contracts, because it is so heavily optimized. So I encourage people to check it out.
[00:40:42] Sina: What's cool about it in terms of the optimizations.
[00:40:45] 0age: just lots of branchless logic. In place of conditionals. Jumps are, are relatively expensive, particularly like loops through stuff. Um, if you can replace that with logical operations, it cuts down on overhead. And, I think the, probably the biggest optimization is in the way that we are deriving the order hash. So you have, you have this whole EIP 712 payload that the particularly because you have arrays inside, you have the offer and consideration, which are both arrays, the derivation of that in a naive format is quite inefficient where you actually have to, if, to include a type hash for each struct in that array.
And then you have to. Hash each item. And then you have to concatenate all the hashes. You have to hash that, and that is what you actually end up providing. So what we do is we read directly from call data. We know that there's going to be given the size and the type of the order. We know exactly that there's going to be only one offer item.
That's a requirement of basic --
[00:42:15] Sina: because it's a basic fulfillment.
[00:42:17] 0age: And we know that the consideration items there's going to be consistency. Like if, if you're taking fees, ERC 20 tokens, like all the consideration items beyond the first one are considered -- are going to be your same ERC20 token. Things like that.
[00:42:33] Sina: You kind of know the shape of the call data ahead of time
[00:42:36] 0age: we know exactly
[00:42:37] Sina: and you can kind of slice the bytecode in a particular way.
[00:42:39] 0age: So, what we do is we ensure that the encoding that's passed in to that call is standard. You can do unconventional encoding in, uh, with like the ABI encoding, because a lot of, lot of arrays and, and dynamic bytes, they actually have pointers that, that tell you, okay, don't look at this section of call data.
That's where the data starts. And then it has a length. That's like the head of
[00:43:04] Sina: Right. Right. Yeah.
[00:43:07] 0age: you can do weird stuff. Like say, I have two duplicate arrays. I could actually just pass it the same pointer to both. And they don't actually
both need --
[00:43:16] Sina: Instead of like rewriting it a second time.
[00:43:19] 0age: Yeah. And there's all kinds of unconventional ways you can do this, but we basically say, no, you have to use the conventional standard coding for this particular call that you would get from any, any library that you're not doing something funky under the hood. So we ensure that that's the case. And then we can use fix call data offsets and memory offsets. Every single time, If you're working with like free memory pointer and dynamic call data that you can’t -- it's not always immediately clear where those, those, like the offerer is always going to be in the same place. The zone is always going to be the same place, that kind of thing. Then you have to use that pointer. You have to add to the free memory pointer, [inaudible], and you have a lot of redundant copies in memory particularly on hashing side. So what we do is we basically just say, go to this exact specific offset of call data point, and this exact specific offset of memory.
Do that. Copy this whole region of call data into this region of memory. And then reuse whatever sections we can. So like, oh, we just calculated this particular consideration items hash, and we know that these deals haven't changed, including the type hash and whatever. So we're, we're just going to put in the new fields, reuse that same memory region, get another hash.
And we also like, as we're going through the loop, we're preparing the event data at the same time. So because on the order you actually specifying the order item or the consideration, those have a start and end amount attached to them, which if the, if the order is like, depending on when it's, when it's fulfilled, there's a linear fit, that's applied.
So you can do ascending descending amounts.
but when you're actually looking at the event or the execution, you just had, all you cared about is what was the actual spent amount or the actual received amount or the realized amount. So you have to do some kind of, we transformed the type a little bit on the way in and the way out. So we create all of that as we're going. All of this is to say basic order fulfillment is heavily optimized, very excited to,
[00:45:47] Sina: It makes me, it makes me think of like, think of like what it takes to write a compiler, you know, with like [inaudible] and like just your you're fully exist in the bytecode moving things around between, you know, in that case, it would be between different, uh, stack locations and what not.
[00:46:06] 0age: uh, I have. Immense immense respect for compiler devs. It is a truly mammoth challenge. and they just crush it
[00:46:21] Sina: Is this, is there some crossover in skillsets, like are compiler developers a good fit for writing very optimized solidity contracts? Because to me it feels like it's, it's basically the same kind of work except applied to more of an application level domain.
[00:46:39] 0age: Yeah, I think that there, uh, there's huge crossover and skillsets, understanding the EVM at a very low level and, and knowing the shortcuts that you can take, given certain constraints -- that you couldn't take in a generic way. Like if you're, if you're just accepting -- memory management is a fantastic example of this, right?
So the example I gave for reusing memory regions is assuming that we know that this particular region of memory can be reused. These fields are consistent across them. For instance, the compiler can't really know that unless you can instruct it somehow of that. So what compilers will do effectively right now is that they will just allocate new memory and then copy the existing stuff, [inaudible] needs to go over into that and operate on that.
So there's a lot of, there's a lot of redundant copies and unnecessary memory expansion, but it's also somewhat unavoidable if you're going to be operating in way that's safe. And memory safe for whatever you throw at it. One thing that I found pretty interesting about Vyper as the, um, learning more about it in the library working group on Seaport is that Vyper takes a slightly different approach than solidity where it actually allocates static regions of memory.
And that, that definitely makes it more memory safe in many ways at the expense of capping the size of memory that you can use in that region. So I think it's, it's a pretty cool different paradigm, um, though, arguably, depending on the, whether or not, you need really big or really small memory, depending I can see that being more problematic.
[00:48:52] Sina: do you do gas optimization at this level? Um, it feels like it's only going to become more and more relevant if you're deploying something on mainnet, especially as the block space keeps -- is like, you know, eaten up by roll-ups and there's more and more adoption. And I think you're right in that certain things, at least for the foreseeable future, we'll continue to settle on mainnet or even if there are L2 centric protocols, they might like, they might anchor onto main net in some, in some way.
Um, so there,
[00:49:25] 0age: One thing I want to say say everyone understands that gas optimization is going to be. Increasingly important in the mainnet context.
But I don't think people quite understand is that gas optimization is going to be equally important in an L2 context, because it's like that analogy of you build more lanes on the freeway.
And now there's more cars.
[00:49:48] Sina: Yeah.
[00:49:48] 0age: If, if these things are going to grow and scale out, then you want the bottleneck to be on the number of people that can be using it. And not on the, the efficiency of the stuff that's running on it.
[00:50:06] Sina: And, and because they're anchoring onto, because they're using mainnet as a data availability layer, at least currently, the scaling gains are only linear. Right. Um, but why do you think, I mean, why is this different than how the internet evolved? Like at this point we don't really care about how much, like we're not optimizing our web apps, even though they use bandwidth between all of these server clients, uh, calls and whatnot.
[00:50:34] 0age: Well it's every time that I visited a website, it caused the website you were visiting to load a little slower than I think we would be optimizing for that.
[00:50:43] Sina: It's true.
[00:50:44] 0age: It’s a shared resource. Now I do think it's important to step back because there are, there are places where it makes sense to optimize for gas.
If you do it, it's really important that you are highly considerate and that you really take the time to document exactly what's happening to understand what's happening and to get as many people to review it as possible. Not just talk to me. Hit me up. If you're, if you're doing-- I will do my best to connect you to whoever I can and will help review --
[00:51:27] Sina: Right. That, that trade-off of like how easy to understand the code is, is like a very important one when it comes to security. Right? Of like all bugs are shallow with enough eyes on them. Like, you don't really get that. If no one can understand what the hell they're looking at.
[00:51:43] 0age: I have. I think it's very important that more people understand assembly because it's going to a value additive, even if we're not writing more assembly collectively for everyone to understand at a lower level how these things work and to be able to review the assembly that is written, and.
Yeah. So I think it's really critical that we all level up in our arms standing of this low-level behavior of the EVM, if we're going to be building on it, the done that being said, it's really important to take a step back and ask what needs to be optimized, what doesn't to be optimized. Right. We made the conscious design decision with Seaport that okay -- Deploying Seaport, right? Deploying new conduits, changing ownership of conduits, like transferring ownership. These are not things that need to be gas optimized.
[00:52:47] Sina: they don’t happen that frequently.
[00:52:50] 0age: yes. What matters is having additional assurances and security checks. Before you have product market fit - spending cycles on gas optimization is probably not ideal, right?
Like ensure that it's something that people want to use. And lastly, don't even begin thinking about gas optimization until you have a full test suite and a complete protocol. It's so easy. And I'm speaking from personal experience here. It's so easy to get tunnel vision and just focus on making that one route a little cheaper before you have all of the pieces in place.
And it's just distracted from the core objectives. And besides it's much -- you can iterate with more confidence. Once you have a comprehensive test suite that you can run and finally, I do think that the biggest gas optimizations are not necessarily going to come from porting to a different language or using assembly or whatever.
It's, it's examining the assumptions that you're making. What can we do off chain versus on chain? Great example with Seaport is we started out being like, okay, here's a number of orders. Let's derive the optimal set of fulfillments on chain. And it was like, yeah, cool. This is awesome. But we just spent more gas deriving the optimal film than we would've saved by just making all the transfers in the [inaudible] fashion.
And another really interesting thing about this is if you have a numb -- like the way we've been talking about the fulfiller -- order fulfiller and basic order fulfiller, which are the sort of standard methods, and this is actually a great segue into the other types of fulfillment. So you've got the mirror, the just fulfill order and fulfill basic work, which are effectively taking a mirror.
[00:55:03] 0age: Basic order does has some extra juice
[00:55:08] Sina: You're taking a single individual offer, you're mirroring it. You're adding your own consideration items into it and a recipient and it's just executes because it's pretty straightforward.
[00:55:19] 0age: Exactly. It's pretty straight forward. So we've talked at length about the, the basic premise of you want to create a listing and you want to fulfill a listing, but now I want to get into, what about if you have multiple listings that you fulfill, and then I think we should also talk about some of the interesting, additional tools, in terms of what you can specify for a specific guidance.
But first let's talk about order combiner. So this is near the very top of the inheritance tree. The order, combiner implements, a series of functions. First we'll talk about fulfill available orders. So fulfill available orders. This is effectively. You want to sweep the floor. You want to buy a number of NFTs in one shot.
Now, the way that this is handled by most aggregators today is that it's like a multi-call. It does it in a loop. It will fulfill each order, according to whatever the logic of on that marketplace. And perform all of the transfers for each of those. what this requires though, is that as the fulfiller that's calling those, that you have, you have to catch any errors that happen in path to perform all the transfers.
And sometimes those errors happen pretty late in the chain of command. Like it's not until you actually get to transfer the tokens that they don't break or something to that effect.
[00:56:59] Sina: Right. Like you realize that the person no longer owns the tokens or
[00:57:03] 0age: Something like that. Um, what Seaport enables is the standard, the standard case here is that I'm trying to buy NFT someone else's already bought. That's, that's really the number one failure case, especially brand new NFT has just dropped. And it's first-time people that are, are trading it in secondary markets.
So what you can do is you can give a whole, whole list of orders in the order that you want them to be attempted to be fulfilled, and it will go through and it will check and say, has this order already been filled? Has this order been canceled? Has this order expired?. Any of those things are true. We'll just pop it off the list. Skip this order. And then we're just going to take, whatever's remaining up to a maximum fulfill. So if everyone's kind of trying to buy the same floor NFTs, then rather than just, I want to like a five, I'll get five someone else's probably doing the same thing. You can give 10 or 15 or as many as you like, so that it if those first five are taken, then you do the next one, that kind of thing.But you're going to stop once you get to five.
And so you can specify, I want to fulfill all of these at once, and then you can do it as one big batch, but effectively you're creating a mirror order for each
[00:58:37] Sina: got it. I would be curious to talk through when a user comes to use open, see the websites, right? Like I either list or I'm buying an NFT, what is the actual like flow that's happening through all of this? I think it would just, it would just help with like, understanding it, um, to talk through the lifecycle and actual trade going through.
[00:59:00] 0age: So when you, when you go to Opensea if you wanna list an item, OpenSea pretty much is just going to, it's like a, a set of tools that it's going to make it easy and, and presentable to construct a Seaport, uh, listing and sign it, present that interface in a trusted context, a domain that you can have strong assurances that you're going to be served the payload that you intend to sign. The good news too, is that there's even additional assurances now that the payload itself is much more [inaudible]. The domain is -- it's part of the [inaudible] specification, and you can see exactly what you're signing, but that's on the creation side. On the fulfillment side, OpenSea is basically just indexing what's available and helping users to discover what is out there.
Um, particularly in the context of these are the NFTs that I own. And so I'm curious what, listings and offers are relevant to these NFTs or the NFT I’m interested in buying. Are the listings available for these? So it's facilitating discovery. And then that, once that discovery is taking place and then the fulfiller is – decides they want to buy or sell.
Then at that point, it's sort of the same premise where OpenSea sort of provides the tools. For you to affect that transaction yourself. Now, at that point you actually don't even need OpenSea., it's really your submitting it directly to SeaPortat that point. So yeah, Openseas, it's more serving in this role in discovery and --
[01:01:01] Sina: A trusted tool for like generating these signatures and um, basically viewing the protocol layer data.
[01:01:12] 0age: that's right.
[01:01:13] Sina: And So openseas, like opensea has a fee, right. Has a 2.5% fee. Where does that, where does that come in?
[01:01:21] 0age: whenever you are dealing with Seaport, Seaport does not have any notion of fees as part of the protocol. but the way that Seaport represents fees is via consideration items. So you name -- you just name any additional consideration items.
The recipient being, whoever fee is going to go to. And this is actually,there's a really interesting feature on Seaport that we call tipping. When you originally create the order, it has some number of consideration items, it could have none. Like I can just offer somebody and that's effectively like a donation.
Right? You can also create something that's just consideration item and no offer. I'm asking you to -- . You get the idea, but like you create an order that has a certain number of considerations. Now, as the fulfiller, every one of those consideration items must be receipt back by the recipient.
However, there's no requirement that you can't add additional considerations. You can tack on the tip. this enables the fulfiller, might be in an entirely different domain that then where the order was originally created, can apply their own tips. And you can extend this really powerful way with zones too, because the zone is a restricted order.
Can now examine the consideration items, which can be dynamically set at the time of the order. - it can then apply [inaudible] that like - Like, oh, I want to validate that, that this tip is in line with whatever custom rule set around how this tip should be applied. Maybe, maybe the creator wants to be able to update their, their recipient address.
And so then you can check that at the time of work fulfillment supply as a tip zone makes sure that it's up-to-date
[01:03:34] Sina: Got it. So, so, uh, like OpenSea, the website and the company that's interfacing with SeaPort the open protocol. The way OpenSea makes money here is when an order is fulfilled. Uh, if it's being fulfilled via the OpenSea, it will tack on an additional tip that goes to OpenSea itself.
[01:03:59] 0age: way that things will be structured at least. At least at first, is that when you create an order on OpenSea, that a consideration item will be added,
[01:04:15] Sina: Yeah,
[01:04:16] 0age: Yeah,
[01:04:17] Sina: that makes sense. I mean, you want to, even from a protocol design point of view, or from an ecosystem design point of view, you want to incentivize both the third parties who are a source of like offers being created and the third parties who are a source of like, fulfillment's happening. You want both of them to be incentivized, right?
So whether it's Opensea or it's, you know, a, let's say there's a smart wallet that allows people to directly trade NFTs in there. You can, um, when you create an offer for a particular user, you could include an additional consideration item, which pays out the wallet creators, for example,
[01:04:56] 0age: exactly. And. That way, it just gives, it gives us a lot more flexibility. And I think will encourage different kinds of business model that can cater to specific types of users that while everyone can still benefit from a shared liquidity. So if you're more comfortable using a site like OpenSea, then that doesn't preclude you from also being able to view and, um, create listings that another site could, um, could easily by building on top of the same Seaport protocol.
Now they're all broadly compatible.
[01:05:42] Sina: Yeah. Um, awesome. So maybe quickly recapping all the different pieces of this. So there's, uh, ERC 1271 bulk listings. There’s this idea of, uh, consideration items. tips, uh, which allows a lot of flexibility in additional, you know, and, and third-parties like being involved in how these offers and fulfillments are entering the, the kind of like decentralized protocol.
Um, there's this notion of conduits, which are, basically responsible for holding all your permissions and figuring out successful kind of trades that could go through, There's this concept of zones, which can be used in, in, in different ways to, as a final kind of Boolean check on whether a complete that order can actually like execute.
and there's also the fact that, fulfillment don't need to basically happen in this one-to-one way. They don't need to fulfill just like one particular offer. They can, uh, you know, you can fulfill multiple offers as long as the, the global kind of like mapping of them all checks out. So if I didn't --
[01:07:06] 0age: a good overview. The two pieces that I want to touch on next that we still haven't gone into the nitty gritty on our criteria based items and partial fills, both juicy topics. We'll start with criteria. you can always name a specific token ID or identifier that you are interested in. I want to buy Bored Ape 1, 2, 3, 4, but one thing that users are really, asking for, and that is a popular idea that's that's gaining traction. Is that well -- I'm actually okay to buy any number of bored apes, any bored ape with an astronaut costume or any board ape period. Collection level and trait level offers. So criteria based items enable you to do that on the offer or the consideration side. And the way it works is that instead of giving a token ID that you care about, you give a Merkel root that contains all the token IDs, then the fulfiller provides during fulfillment, they choose what token ID they want and give a proof alongside. Then you can prove that that token ID is contained inside that Merkle root and resolve that criteria-based item into just a standard item. There is a limitation with the current version of Seaport, where you can only, you can only resolve a specific item once as part of any single order. Which then ties in very nicely though, to partial fills just like you can choose whether or not order is it restricted order and needs to check a zone to see if an additional rule set is adhered to, you can also specify the, a given listing has partial fill support.
The way that partial fills work is that every single offer item and every single consideration item can be scaled down. As long as they're scaled down by the same fixed amount. Theres a fraction that's -- the way that you do criteria based and partial fills is you call the advanced version of whatever the function is.
So there's fulfill orders, there's fulfill advanced orders, there's match orders that match advanced orders. Right. Um, fulfill available orders, fulfill available advanced orders. And those, you actually get the numerator and denominator for each order saying, this is the fraction I want to fill, and you can also give criteria resolvers.
So the only rule with partial fills is basically that everything needs to exactly [inaudible] there. So if there's any remainder after applying those fractions, it's not gonna work. So the way that you structure that, if you're trying to build something that does partial fills is you start with, what's the unit piece of this? And then you apply some, some number on top of that.
So if I had 10, 1155s that I was trying to sell, I would start with what's the price for selling one of them. And I multiply both the offer and --
[01:10:45] Sina: right. right. Like you're not, you're not going to fractionalize any NFTs or anything like that, that they are, they're gonna remain in whole number
[01:10:53] 0age: Yes. And NFTs are, they are non fungible as, as the kids say. the, another interesting point of intersection is with, I mean, we touched on this earlier – is ascending and descending amounts. You can have a start amount and an end amount for any item. And as the, from the time that the orders start time and end time, you just find a linear fit and apply that. The way this intersects with partial fills is that you apply the partial fill amount to both the start and the end amount, and then it will apply to the linear fit.
So that's pretty much all that's the grab bag of features.
[01:11:40] Sina: So basically, with the criteria based, offers you have a lot of expressiveness in what you request, as a consideration, right? And so you can, I mean, if you can put a Merkle root on there, you can basically make that Merkle roots include whatever leaves that you wanted.
So you get full expressiveness and like what, what you'll accept, uh, in return for this offer, um --
[01:12:12] 0age: It’s pretty cool because you can then, particularly if you're leveraging contracts with 1271, you can build primitives where you actually can have Oracles for floor prices, because you can say, look, here is a collect a standing collection level bid with this price that any holder of this NFT can leverage. So now I can take that and actually use that and extend on that and, and build
[01:12:45] Sina: sounds. I mean, it just that's the first thing it made me think of was this idea of retroactive public goods funding, where like some ecosystem entity could basically come in.and put a floor, uh, in place for some NFTs would be like, you know, anyone who contributed to this public goods project got an NFT at some point in the past.
And now we're just gonna like put a floor there. And I mean, this is, this is its own rabbit hole, but you could totally implement some of those things very easily using this protocol.
[01:13:17] 0age: Yeah. Another thing that I really get excited about is you can combine, you can combine criteria based and partial fills with gasless listing. If you think about the way that the OpenSea shared storefront by default, the opensea shared storefront already gives a user's proxy, their opensea proxy, approvals. a side note, no user proxies anymore, which is going to be a big, big win.
Not having to deploy your own contract just to this. So you can buy from the shared storefront without having to pay gas to bootstrap what's going on. And then it does lazy minting. So you actually buy something – its only once it gets transferred, that it triggers a mint. Now you can have NFTs creators actually deploy their own contract and do the same kind of thing and sign a single order, and that order now can be shared with marketplaces like Opensea, and you can effectively do a primary sale directly through OpenSea and not have to go to a sketchy mint-site and approve some new token.
It's just right out of the box. You have a way to, to run a primary sale. And how to populate directly in other markets by saying, oh, I'm just going to sell 10,000 NFTs. You can pick any NFT and purchase it. And, um, and you just fulfill the order. And as part of the order fulfillment, there's a token transfer hook that, that mints.
[01:15:15] Sina: Wow. Interesting.
[01:15:18] 0age: So that's the kind of thing that I'm excited to see people build
[01:15:20] Sina: yeah. So primary issuance could just be it's kind of lazy primary issue. And so you say that these, this is what they're going to look like. This is, you know, this is the functionality they give. This is what I, what we want as a consideration to consider these orders fulfilled. And then whenever each single one of those orders is fulfilled, then that hook actually like mints the NFT for the first time.
[01:15:48] 0age: Yeah, it would basically be like, in order to meet this NFT, you just have to buy it from the creator via Seaport, using this single standing partially fillable criteria based bid. And I can just take one and buy it and, and that's how I mint. And you can display exactly what's being minted.
I think it will really work well for, membership pass type mints. And like, if you have an 1155 or something like that, it's, um, that's where it would be really cool.
[01:16:26] Sina: totally. Um, so yeah, I feel like we've probably covered the different nooks and crannies of the protocol. Um, though there seems to be more to it every time I think we have it. So
[01:16:42] 0age: there's a lot. Yeah. But it's also, it's great that like, I love this longer format where we can actually get into the weeds and actually outline a pretty comprehensive, summary of what's going on.
But I will also say is that if you have questions about how other things work or ideas of how to improve on this, we’re all building this together at this point, like this is now very much a community endeavor, and it's been really awesome to see how many people have just jumped right in, like join the convo on Twitter, open issues and pull requests on the repo, slide into the -
[01:17:29] Sina: join the holy war between viper and solidity. .
[01:17:32] 0age: Yeah, this is something that we're all building together. It's I would not anticipate Seaport, just being a static thing. Like there's going to be, there's going to be new versions and, and there's a path to support these new versions two via conduits. So, we're all building this together and this is just the first step.
[01:17:55] Sina: Yeah. So, so maybe as a, as a last topic, zooming out of the architecture of the protocol and how it all works. do you have any ideas of just new types of things that people should be thinking about or building on top of this and things that you, you know, don't necessarily make sense? Like just funky or like weird little ideas, just directions that might be interesting to explore.
[01:18:20] 0age: Yeah. So, as I mentioned earlier, zones are being slept on. I would love to see more exploration around what could be done with zones, like the primary sale thing that we were talking about, how the zone implements a white list based on you submit a proof that you're in a Merkle root. that's a pretty awesome, a pretty awesome way to, run a primary sale, especially that doesn't add bloat to the contract itself, because you've put all of that logic in the zone and you can reuse those across different projects too if there's like a shared updatable, um, whitelist for different projects. the zone stuff seriously, sky's the limit. That's that is like the fulcrum for extensibility that people should be exploring and thinking about. Another thing is if you have an idea for like an in game marketplace, Collection specific marketplace, a marketplace that’s geared around power users or a particular user segment or collection, or you name it. I'd strongly consider looking into Seaport and exploring, leveraging it. Because even if you're not interested in the, in all of the features, there's almost certainly a way to adapt it, to do what you need.
Just get your head around the mental models of the way that offer and consideration works. It's very expressive and flexible in that manner. And, and to benefit from all of the, the tooling and the community,
[01:20:16] Sina: And the liquidity.
[01:20:18] 0age: Yeah, and, and that liquidity then can be fulfilled outside of your marketplace.
You're going to originate it on your workplace. You can show liquidity from other marketplaces on your marketplace, and you can do that in a much more, in a way that's way easier to wrap your head around is going to cost a lot less gas and ultimately is going to make for a more decentralized and user centric model where users remain in control of their own assets, but also can transact in a way that you don't have to trust the other party there's this mediation layer that we've all, we've all applied various learnings from various pain points.
and built a foundational layer that is robust and flexible. That's where I see this going. And, I'm really excited to see what people building.
[01:21:25] Sina: Yeah, me too.
[01:21:27] 0age: yeah, and I will have more updates on, on the Seaport front. We're just getting started here.
[01:21:34] Sina: just getting started.
[01:21:36] 0age: Thanks, Sina.