Jump to content
hammertoe

Transaction memos and orders

Recommended Posts

I'd like to keep some kind of metadata associated with an order placed on the network (OfferCreate). The problem is that the memo field is attached to the transaction not the offer, and as far as I can see there is no easy way to go from an account and sequence number back to a transaction via the Ripple API? I see there is a method on the Data API, but not the main Websocket API?

The use-case is that I subscribe to the transactions stream and will see a transaction related to my account. I can see what offers may have been modified or deleted by that transaction, but unless I'm missing it I don't see a way to then go from there back to the original submitted transaction and memo. I'd have to keep a local copy of the sequence number of every offer I submit and mapping it to the txn hash so that when I see a transaction come in that crosses my order I can look up the metadata. Seems convoluted, and storing locally seems to defeat the point of the memo field.

-Matt

Share this post


Link to post
Share on other sites

Well, yes I could do that, but it would mean keeping state locally, and so far my application has managed to avoid having to keep any state at all. Also if the application were distributed or their were multiple parties involved it would be a hassle.

So, in short, yes I could keep my own state, but was thinking that maybe I was missing something and there should be a way to be able to get from the information in a transaction that deletes/modifies my Offer Node back to the transaction of mine that created that Offer Node? Either that or a way to attach memos to an offer itself, and not just the transaction that created it.

-Matt

Share this post


Link to post
Share on other sites

Statelessness at the user end is highly useful and most decent crypto exchanges let you supply a client order id, sometimes up to 32 bytes. You can hack it with ripple by using the Expiration field in OfferCreate. Calculate a ripple epoch time for a month or a year ahead of a fixed time around now, and then add your own id to that number. When you want to inspect it, subtract that constant. That should give you around 3 billion usable numbers to play with.

https://ripple.com/build/transactions/#offercreate

https://ripple.com/build/rippled-apis/#specifying-time

Share this post


Link to post
Share on other sites

@donch ahhh cunning plan. I was thinking of doing something similar with using the high precision of the amounts to a similar degree as I'd only really need to store a few flags, but there is always a chance that could get rounded out somewhere in the floating point maths. I am already using the expiry time, but the idea of using an epoch or even just using the seconds field would be sufficient for my means.

However it still wouldn't quite solve my problem I don't think. I want to be able to get this information from the information contained in the transaction that crosses my offer. And alas the only information contained in the... 

I think I have just solved it. I have just seen there is a field called PreviousTxnId in FinalFields of the Modified/Deleted Node. This is the txn id of the transaction that created the node that this transaction is now modifying/deleting. I can call getTransaction with that and retrieve my memo that way.

-Matt

Share this post


Link to post
Share on other sites

PreviousTxnId will work if you are only interested in checking metadata when the Offer changes, i.e. you cancel it, replace it or someone else consumes it. If you want to get the state when you do account_offers, such as when restarting your bot, you don't get that field. Just tried it with account_objects and the field is supplied, although for some offers the PreviousTxnId is all zeroes. Don't know if it is a bug or not. Solid way to work is embed the account sequence for the creation transaction into the expiration field 'extra' data. You'll always be able to get the transaction then, even if you have to page through account_tx results. 

Far better would be to have a custom 32 byte field in Offer where you can embed whatever you want. Use case: market maker wants to freeze their external price into the offer, and maybe a datetime for that quote. Makes analytics so much easier, rather than reconciling two versions of the truth.

Share this post


Link to post
Share on other sites
11 hours ago, donch said:

Far better would be to have a custom 32 byte field in Offer where you can embed whatever you want. Use case: market maker wants to freeze their external price into the offer, and maybe a datetime for that quote. Makes analytics so much easier, rather than reconciling two versions of the truth.

This is pretty much my use-case. I want to add some metadata to the offers for my marker maker bot so that I can later analyse which offers/trades were most successful. Yes, I could keep it all locally, but a simple 32-byte field as you say would be very helpful.

@JoelKatz is there some kind of mechanism for putting forward feature requests like this? I mean, I know it's all OSS, but it would be good to get some kind of idea of interest if a feature might be useful and/or what the ramifications might be. I very much favour stability (of API/data structures) over feature bloat, and would rather Ripple keep things focussed (c.f. the current Ethereum issue). But I would have thought that an extra memo field might be desirable. 

-Matt

Share this post


Link to post
Share on other sites

The sequence number of the transaction that created the offer is encoded in the ledger in the offer object. Here's what an offer object looks like on the ledger:

Quote

...   

    {
          "Account": "rGcQo3R21J8BpextdCS1KNhweUJht6c8Pv",
          "BookDirectory": "4A0E089535EDF72C05C715E5F8999DB212DC48D18498AC715503E41F57F85EE3",
          "BookNode": "0000000000000000",
          "Flags": 0,
          "LedgerEntryType": "Offer",
          "OwnerNode": "000000000000004B",
          "PreviousTxnID": "D7A8B831063B726170819F23A0E1A54AAF9326E18B9A8AE6DC5879194BE71D3B",
          "PreviousTxnLgrSeq": 18428127,
          "Sequence": 2454,
          "TakerGets": "9471039",
          "TakerPays": {
            "currency": "CAD",
            "issuer": "rJq7X41CpA8rZ5JRPdYSqL3DQjfQKwZSzm",
            "value": "10373138.4277344"
          },
          "index": "0000285186F3D87B9418986B8782693656E2CCACFA134B08C5BD7CA03651D99D"
        },
...

 

Notice the "Sequence" field. That's the sequence of the transaction that created the offer. Also, encoded into the "BookDirectory" (last 64 bits) is the rate at which the offer was placed. You can retrieve these with the "ledger_entry" RPC command. You can specify an "offer" object with an "account" and "seq".

Share this post


Link to post
Share on other sites

Yep, the sequence number is also in the account_offers response, so embedding it in the Expiration field is pointless. The quality field is also included in that response, so you can calculate the original ratio without the ledger_entry call.

That wasn't really the spirit of the original question though. Say you have 100 offers on different markets, that you regularly update. You want to store the original "external" price you derived your ripple price from and the time when you extracted it and maybe some other small number of bytes of data so that you can do later analytics of what markups worked well. If you depend on the memos in the original transaction, you are going to be executing a large number of API calls to get the original transactions. The Offer ledger entry itself is the obvious place for that data. Currently there are some "spare" bits in the Expiration field, much more useful would be a dedicated field. 32 bytes would be more than enough. I'd even be happy with just 8.

The other question was how to formally submit feature requests, which remains unanswered.

Share this post


Link to post
Share on other sites

@JoelKatz Ahh, OK, that helps. That method is a websocket command too, but AFAICS is not exposed by the RippleAPI. Or at least is not documented in the API as far as I can see. As Donch says, having a field come back with the order itself would be very convenient too though.

Thanks!

-Matt

Share this post


Link to post
Share on other sites

I would argue that you should store private information that only you care about in a database, not on a public ledger. But you could use the 8 least significant bits in the Expiration for this purpose. Presumably, you don't care about precisely when your offers expire.

Share this post


Link to post
Share on other sites

It would be an interesting thing, but I'm for leaving the ledger as light as possible.

To 'officially' send requests I think github is the right way. But all my requests in rippled and ripple-lib were never considered :)

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×
×
  • Create New...