Skip to content
Currency Value Information
Share
Explore

Currency Value Information

Hi Philip!
You previously asked these insightful questions:
1. The conciliation premium is apparently split across all currencies. Is this split evenly according to some normalized exchange rate? Or could it make sense for the split to depend on the payer's endorsements?
2. For that matter, the system apparently needs to know that X amount of ETH is "worth" $36. Where does this information come from?

In other words, you’re pointing out that the index payment function looks something like this:
def ixpayment(wallet, quantity):
"""
quantity (float): the amount to transfer
wallet (vector): the tokens owned by this wallet
"""
return wallet * (quantity / total_wallet_value)

But the question this begs is how do we calculate the total_wallet_value? One way we might imagine doing this is by defining a function called value which accepts the wallet and the valuation of each currency in order to find the total wallet value:
def value(wallet, valuation):
"""
wallet (vector): the tokens owned by this wallet
valuation (vector of len(wallet)): the valuation to use for each token
"""
return sum(wallet * valuation)

This would allow us to update our index payment function to:
def ixpayment(wallet, valuation, quantity):
"""
quantity (float): the amount to transfer
wallet (vector): the tokens owned by this wallet
valuation (vector of len(wallet)): the valuation to use for each token
"""
return wallet * (quantity / value(wallet, valuation))

However, this still leaves open the question: where does that valuation come from?

I originally gave you a pretty bad answer:
You might imagine we start by assuming that there is a global market for each currency. In that case, yes we can just accept the market rate. After all, if the market rate for currency i were below the vendor’s rate then the customer might be tempted to arbitrage by buying more currency i. But quickly this becomes untenable as all currencies become bundled. In that case, a new mechanism will have to be found to determine local market value. One tempting way for a shopkeeper to determine this is to follow their supplier’s endorsements proportional to the amount they buy from each supplier, and to follow their customers proportional to the business of their customers.

Essentially, what I was saying is that initially we could imagine a market that is deciding those valuations, and just inherit from there. We might imagine expressing this idea in code like:
ixpayment(wallet, get_market_values(wallet), quantity)

However, this is only a short term solution. Perhaps the key reason why this won’t work forever is that the whole idea of Index Wallets is that different people can have different valuations, whereas if you get it from a market then there’s only a single universal valuation.
What we would prefer is if it were somehow ok for the wallet to merely incorporate the local preferences of the customer and the vendor when calculating the value of their wallet.
This is what you were referring to when you said:
Or could it make sense for the split to depend on the payer's endorsements?
It turns out the answer is yes! And the result is surprising. Let me show you what I’ve found.

Valuations Replace Endorsements

First, as we go through here I won’t talk almost at all about endorsements, despite how important they were in the original article. That’s because I think I’ve found a better framing by using valuations.
If you recall, an endorsement (e) was the percentage that a vendor valued the currencies in a customer’s wallet. 100% meant they shared the same value for their currency. 50% meant the vendor valued the customer’s currency half as much. 200% meant they valued it double. Proving that endorsement dictators (people with control over everyone else’s endorsements) have a reason to increase their endorsements despite the inflation they incur was a key result of the original article.
However, the challenge with endorsements is that they only exist where there’s an interaction. So only when a customer buys from a vendor can we calculate an endorsement. This is where valuations become nice to work with because we can define them to be the amount that the players personally value a currency, and then we can use this to recover the endorsement when they finally interact. I.e.
Where v_s is the supplier’s valuation and v_c is the customer’s. This saves us from the infinite regress. With that out of the way, we won’t mention endorsements again in this article, but at least you know how to get them back if you need.

Framing the Question

The question we’re trying to answer is: can we use the valuations of players as the basis for partitioning their payments?
To answer this, it’s worth considering what could go wrong. Here’s what I’ve come up with:
perhaps a player could lie about their valuation, thereby paying less (or charging more) than they would have if they were honest
perhaps a player could lie about their valuations in order to get rid of a currency they don’t want. This would break the proportionality property of index wallets and we would degrade back to an arbitrage system
These are deeply intertwined issues. Here’s an example of how that might play out in a story:
Marie walks in to Charlie’s shop and places the running shoes she wants to buy on the counter. Charlie rings them up and tells her the price. Marie has in her wallet some currency she really doesn’t want, but she knows how to cheat the system. As she opens her phone to prepare to make the payment, she temporarily increases her valuation of the currency she doesn’t want so that it will be considered as a larger part of her wallet. This way, when she makes the payment, a larger proportion of the unwanted currency flows out of her wallet and into Charlie’s. Now she resets her wallet to its original valuation.
Certainly, it loses points for being inconvenient for Marie. But it seems somehow worse than that. If Marie is able to offload some of her unwanted currency just by engaging in strategic purchases, then you know that in the limit there will be a sophisticated system of markets which would allow you to pay into a trader’s wallet, they go and make other arbitrage payments on your behalf, and then they’d return to you the currency you wanted (of course, after they had taken their fee). In other words, it would seem to break one of the properties of Index Wallets that is so promising: speculation is hard. But perhaps it’s worse than that: it directly breaks Index Wallets, because if you can arbitrage, then it’s the same as there being no Index Wallets.

So what can we check to ensure this doesn’t happen? The simple check to perform is this: no matter how a player changes their valuation, they cannot:
change the total value they feel like they’ve paid / the vendor feels like they received
change the composition of their the outgoing payment

Ruling Out Strategic Play by Vendors

One thing I considered in my explorations that proved not to be relevant is the changes a vendor can make to their valuation. They also shouldn’t be able to change the composition of the incoming payment by changing their valuation. However, I’ve come to believe this isn’t as important to certify, for two reasons:
they already have in incentive to “feign” their valuation — they want it to be higher. That’s what we have already proved, they want to increase the valuation so as to attract more customers, so we should already expect them to be at the maximum valuation that is competitive and they can sustain, otherwise they lose customers
I think we can make a simple symmetry argument. If we can prove it’s true that the customer can’t profitably feign, then it’s also true that the vendor can’t feign.

Checking the Simplest Case

To start, let’s run the simplest test we can imagine. Given some player with a wallet that contains various currencies, let’s allow them to select two valuations. One is their honest valuation, this they keep private. The second is their public valuation, this is the valuation they use to feign their preferences in order to divest of certain currencies. We’ll call these honest valuation and feigned valuation respectively. Finally, we’ll give a vendor their valuation, named as such. We’ll use this to check if we can violate any of our criteria.
For the first criteria:
change the total value they feel like they’ve paid / the vendor feels like they received
We want to ensure that when the customer uses their feigned valuation, the value of their outgoing payment is identical to when they don’t feign, not only from their perspective, but also from the perspective of the vendor. We can check these with two equalities:
customer_value_feign == customer_value_honest and vendor_value_feign == vendor_value_honest

In practice, due to floating point imprecision, we actually perform this check:
np.isclose(customer_value_feign, customer_value_honest, rtol=1e-5) and np.isclose(vendor_value_feign, vendor_value_honest, rtol=1e-5)
Where we see if it’s sufficiently close to the expected value. But when you see that in the code, you can mentally substitute for the equality.

The second property we’d like to ensure they cannot violate is:
change the composition of their the outgoing payment
Unfortunately, this is not covered by the previous condition. To find the customer_value under the honest and feigned conditions, we’re passing in two different payments (which we can think of as mini-wallets, or wallets in transit) and comparing against the honest_valuation.
customer_value_honest = value(ix_honest, honest_valuation)
...
customer_value_feign = value(ix_feigned, honest_valuation)
Our value is defined this way:
def value(wallet, valuation):
"""
wallet (vector): the tokens owned by this wallet
valuation (vector of len(wallet)): the valuation to use for each token
"""
return sum(wallet * valuation)
So if the two index payments come out to the same value, they could have done so either by being identical, or by ever so carefully rebalancing the wallet so that it compensates for all decreases in certain tokens with concomitant increases in others. We’ll need another check:
ix_feigned == ix_honest
Which we write as:
all(np.isclose(ix_feigned, ix_honest, rtol=1e-5))

So in total, the code we intend to run is:
wallet = np.array([20, 30, 50])
honest_valuation = np.array([1, 1, 1])
feigned_valuation = np.array([1, .5, .5])
vendor_valuation = np.array([1, .5, .5])
quantity = 10

ix_feigned = payment(wallet, feigned_valuation, vendor_valuation, quantity)
ix_honest = payment(wallet, honest_valuation, vendor_valuation, quantity)
print(f"{ix_feigned=}")
print(f"{ix_honest=}")

customer_value_feign = value(ix_feigned, honest_valuation)
vendor_value_feign = value(ix_feigned, vendor_valuation)
print(f"when feigning, you feel like you pay {customer_value_feign}")
print(f"when feigning, they feel like they receive {vendor_value_feign}")

customer_value_honest = value(ix_honest, honest_valuation)
vendor_value_honest = value(ix_honest, vendor_valuation)
print(f"when honest, you feel like you pay {customer_value_honest}")
print(f"when honest, they feel like they receive {vendor_value_honest}")

ok = np.isclose(customer_value_feign, customer_value_honest, rtol=1e-5) and np.isclose(vendor_value_feign, vendor_value_honest, rtol=1e-5) and np.isclose(ix_feigned, ix_honest, rtol=1e-5)
print(f"{'OK' if ok else 'bad'}")

However, our code is still underdefined. You can see we’re invoking a payment function, but all we’ve defined so far is value and ixpayment. This is because a single index payment is not enough to compensate the vendor at the price they’ve defined. You’ll remember this problem from the original article, because in addition to the original quantity the customer intends to send in the payment, they must send some extra currency to make up for the fact that the vendor has set a lower valuation than the customer. This extra we call the conciliation:
ixpayment(wallet, vendor_valuation, quantity + conciliation)

Defining Conciliation

The good news is that with the tools we’ve developed so far, the conciliation is quite natural to define. Here’s the definition and then I’ll explain it beneath:
def conciliation(wallet, source, target, quantity):
"""
wallet (vector): the tokens owned by this wallet
source (vector of len(wallet)): the valuation according to the starting wallet
target (vector of len(wallet)): the valuation according to the destination wallet
quantity (float): the target amount to transfer
"""
return value(ixpayment(wallet, target, quantity), source) - quantity

You can see that what we’re doing here is figuring out the amount extra we need to send by first “imagining” we sent an index payment from the customer’s wallet with the quantity that the vendor would consider to be enough
vendors_desired_payment = ixpayment(wallet, target, quantity)
(here, the vendor is the target because that’s where the money is going)
Then we figure out how much that’s worth from the perspective of the customer:
vendors_desired_payment_in_customers_valuation = value(vendors_desired_payment, source)
(here, the customer is the source because that’s where the money is coming from)
Finally, we just subtract off the quantity. By doing this all that’s returned is the amount extra that needs to be sent on top of the original payment, we do this because we want to think about the conciliation as the extra or the premium. You’ll see later on that we have other options for how to frame this. So that’s it:
vendors_desired_payment_in_customers_valuation - quantity
(P.S. I think there are better names for vendors_desired_payment and vendors_desired_payment_in_customers_valuation that might actually be helpful. E.g. what do we call the value of a wallet from a particular person’s perspective? The value in their terms? In their valuation?)

Thanks to having defined conciliation, we can now define the payment function:
def payment(wallet, source, target, quantity):
return ixpayment(wallet, source, quantity + conciliation(wallet, source, target, quantity))

This gives us all that we need to run our test:
def value(wallet, valuation):
return sum(wallet * valuation)

def ixpayment(wallet, valuation, quantity):
"""
quantity (float): the amount to transfer
wallet (vector): the tokens owned by this wallet
valuation (vector of len(wallet)): the valuation to use for each token
"""
return wallet * (quantity / value(wallet, valuation))

def conciliation(wallet, source, target, quantity):
"""
wallet (vector): the tokens owned by this wallet
source (vector of len(wallet)): the valuation according to the starting wallet
target (vector of len(wallet)): the valuation according to the destination wallet
quantity (float): the target amount to transfer
"""
return value(ixpayment(wallet, target, quantity), source) - quantity

def payment(wallet, source, target, quantity):
return ixpayment(wallet, source, quantity + conciliation(wallet, source, target, quantity))

wallet = np.array([20, 30, 50])
honest_valuation = np.array([1, 1, 1])
feigned_valuation = np.array([1, .5, .5])
vendor_valuation = np.array([1, .5, .5])
quantity = 10

ix_feigned = payment(wallet, feigned_valuation, vendor_valuation, quantity)
ix_honest = payment(wallet, honest_valuation, vendor_valuation, quantity)
print(f"{ix_feigned=}")
print(f"{ix_honest=}")

customer_value_feign = value(ix_feigned, honest_valuation)
vendor_value_feign = value(ix_feigned, vendor_valuation)
print(f"when feigning, you feel like you pay {customer_value_feign}")
print(f"when feigning, they feel like they receive {vendor_value_feign}")

customer_value_honest = value(ix_honest, honest_valuation)
vendor_value_honest = value(ix_honest, vendor_valuation)
print(f"when honest, you feel like you pay {customer_value_honest}")
print(f"when honest, they feel like they receive {vendor_value_honest}")

ok = np.isclose(customer_value_feign, customer_value_honest, rtol=1e-5) and np.isclose(vendor_value_feign, vendor_value_honest, rtol=1e-5) and all(np.isclose(ix_feigned, ix_honest, rtol=1e-5))
print(f"{'OK' if ok else 'bad'}")
←(This is the last time I’ll dump all the code in the main thread. From now on I’ll just show snippets of the things I’m changing and I’ll hide the code behind dropdowns like this!)
Yep! this is where I’ll put all the code I ran

Which outputs!
ix_feigned=array([3.33333333, 5. , 8.33333333])
ix_honest=array([3.33333333, 5. , 8.33333333])
when feigning, you feel like you pay 16.666666666666664
when feigning, they feel like they receive 10.0
when honest, you feel like you pay 16.666666666666664
when honest, they feel like they receive 9.999999999999998
OK

So we’re done, right? It definitely bumped up my confidence seeing that. You can see that not only is the value of the currencies under the feigning and the honest scenario identical, but so is the payment array.
However, our confidence can be much higher once we test more inputs:
oks = 0
for _ in range(samples:=1000000):
wallet = np.random.randint(0, 100, 3)
honest_valuation = np.random.uniform(-10, 100, 3)
feigned_valuation = np.random.uniform(-10, 100, 3)
vendor_valuation = np.random.uniform(-10, 100, 3)
quantity = random.randint(1, 100)
...
if not ok:
print(f"{wallet=}")
print(f"{honest_valuation=}")
print(f"{feigned_valuation=}")
print(f"{vendor_valuation=}")
print(f"{quantity=}")
print(f"{ix_feigned=}")
print(f"{ix_honest=}")
print(f"when feigning, you feel like you pay {customer_value_feign}")
print(f"when feigning, they feel like they receive {vendor_value_feign}")
print(f"when honest, you feel like you pay {customer_value_honest}")
print(f"when honest, they feel like they receive {vendor_value_honest}")
print(f"{'OK' if ok else 'bad'}")
break
oks += 1
print(f"{oks} samples ok")

Full code for this found at:

A common output for something like this is:
wallet=array([0, 0, 0])
honest_valuation=array([31.2818237 , 59.7175243 , -1.89516796])
feigned_valuation=array([73.72773384, 54.84279759, 33.43693785])
vendor_valuation=array([ 0.58350452, 10.14517132, 84.78023425])
quantity=92
ix_feigned=array([nan, nan, nan])
ix_honest=array([nan, nan, nan])
when feigning, you feel like you pay nan
when feigning, they feel like they receive nan
Want to print your doc?
This is not the way.
Try clicking the ⋯ next to your doc name or using a keyboard shortcut (
CtrlP
) instead.