-
Notifications
You must be signed in to change notification settings - Fork 118
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ARC-52 Contextual Wallet API #239
base: main
Are you sure you want to change the base?
Changes from 31 commits
f533319
2a8eeb9
fcdd0e4
ba9a4c1
62f7bf6
585d48a
5874b3f
2488fa5
048e508
76899c7
3f4fc71
aa256ab
1fb8d83
0107c5c
326445b
8feb165
9ea1f1d
ca90868
8e29901
8ad26e3
6692a77
f459553
ea9dd28
62b841e
660e82f
b38ef81
4efe105
f36cf8b
8e26a6f
0192f5c
d44a8e9
5766c5f
3ab2169
f66b436
950020c
87d5291
4319a82
6e8337c
8b4de1d
ad3642a
a151b98
718e059
421089f
43aa9c0
ced1c70
b84c6be
1b51687
333601d
e008a3d
5b1a230
c068a44
a6ffb6e
6a02c3f
4bdb38f
92a8b4e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,210 @@ | ||
--- | ||
arc: 52 | ||
title: Wallet Contextual KeyGen and Signing | ||
description: Algorand Wallet API for Contextual KeyGen and Signing | ||
author: Bruno Martins (@ehanoc), Patrick Bennett (@pbennett), Kevin Wellenzohn (@k13n) | ||
discussions-to: https://github.com/algorandfoundation/ARCs/issues/239 | ||
status: Draft | ||
type: Standards Track | ||
category: Interface | ||
created: 2023-06-30 | ||
--- | ||
|
||
# Algorand Wallet Contextual KeyGen and Signing | ||
|
||
## Abstract | ||
|
||
This document specifies an expansion of the Algorand Wallet API to support contextual key generation and signing for **non-algorand-transaction** contexts. | ||
|
||
## Specification | ||
|
||
The key words "**MUST**", "**MUST NOT**", "**REQUIRED**", "**SHALL**", "**SHALL NOT**", "**SHOULD**", "**SHOULD NOT**", "**RECOMMENDED**", "**MAY**", and "**OPTIONAL**" in this document are to be interpreted as described in <a href="https://www.ietf.org/rfc/rfc2119.txt">RFC-2119</a>. | ||
|
||
> Comments like this are non-normative. | ||
|
||
### Overview | ||
|
||
> This overview section is non-normative. | ||
|
||
At a high level this specification defines a new API to generate keys and signing in other contexts than transactions. | ||
|
||
> Interfaces are defined in TypeScript. All the objects that are defined are valid JSON objects. | ||
|
||
#### Contexts | ||
|
||
An enum of contexts that are supported by the wallet. This enum is extensible and can be expanded in the future to support more contexts (i.e messaging, ephemeral keys, other chain derivations, etc) | ||
|
||
```ts | ||
enum KeyContext { | ||
Address = 0, // Otherwise known as Account? | ||
Identity = 1, | ||
... | ||
} | ||
``` | ||
|
||
#### BIP44 Context Derivation Paths | ||
|
||
There is several reasons why we want to avoid key re-use in different contexts. One of the reasons is that we want to avoid the possibility of **leaking** information about the identity of the user. Another reason is that there is some cryptographic security concers when re-using keys or in case of keys being compromised the impact is **compartmentalized**. | ||
|
||
For this reason is advisable to have different derivation paths for different contexts supporting recoveribility of the keys by using the same mnemonic as before. | ||
|
||
| Purpose | Coin Type | Account | Change | Address Index | Context | | ||
| :-----: | :-------: | :-----: | :----: | :-----------: | :-----------: | | ||
| 44' | 283' | x' | x | x | Addresses | | ||
| 44' | 0' | x' | x | x | Identity Keys | | ||
|
||
see <a href="https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki"> `BIP-44` </a> | ||
|
||
#### Wallet New Interface | ||
|
||
This new interface is for the wallet to implement. It is an extension of the current wallet interface. | ||
|
||
This extension is **OPTIONAL** and separate from the current interface that handles address generation and transaction signing. | ||
|
||
```ts | ||
interface Wallet { | ||
... | ||
keyGen(context: KeyContext, account:number, keyIndex: number): Promise<Uint8Array>; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we want to comply with BIP-32 we should be mandatory on the Public derivation indices range from 0 through 231-1. The hardened derivation indices range from 231 through 232-1. Therefore, numbers must be at least 4 bytes. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's hardened within the implementation. accounts are hardend There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am ok with having those well specified in the implementation. However, I would like to be mandatory in the spec as well. IMHO those constraints should be clarified in the ARC-52 spec, or at least should be redirected to BIP-32. My issue with the current text is that an Algorand wallet might be compliant with ARC-52 but not compliant with BIP-32. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. BIP-44 defines the hardening at account / index level. I don't think we should expose that here as this is a wallet API. Anyone that would like to do BIP32 but not BIP44 compliant, should just use a low level BIP32 API to derive keys that way (i.e non hardened accounts or hardened indexes instead) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Regardless of the value that those indexes may assume, which depends on the hardened / non-hardened implementation, the type My issue here is that ARC-52 assumes that the reader is confident with BIP-32 and understands the constraints of that specification. If we choose to make this assumption (which I believe is reasonable), it would be beneficial to explicitly mention it in the overview. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I understand, although i see problems with that. We could expose more options or a more "advanced" API. problem is not every integrator understands the nuances of technical standards that are used underneath. Good API's in my opinion are strict and don't offer too many options that can be miss used. There can be an integrator that understands BIP-32 and another that doesn't. This would lead for implementations that might not be interoperable and recovery in one wallet wouldn't work on another wallet. |
||
signData(context: KeyContext, account:number, keyIndex: number, message: Uint8Array): Promise<Uint8Array>; | ||
ECDH(context: KeyContext, account: number, keyIndex: number, otherPartyPub: Uint8Array): Promise<Uint8Array> | ||
... | ||
} | ||
``` | ||
|
||
##### keyGen | ||
|
||
Wallet API function method that derives a key in a given context. The key should be derived from the wallet's master key. The key derivation should be deterministic, meaning that the same key should be derived from the same context and name. | ||
|
||
In order to derive a key, the wallet should lookup the different derivation paths for the given context. | ||
|
||
- **method name**: `keyGen` | ||
- **parameters**: | ||
|
||
- `context`: The context of the key to be generated | ||
- `account`: The account index to be used for key derivation. The value value should be hardened as per BIP44 | ||
- `keyIndex`: The key index to be used for key derivation. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Bip-44 includes the change type but we are not incorporating it? |
||
|
||
- **returns**: Byte array cointaing the public key, which is a point on the Ed25519 elliptic curve. The public key is encoded as compressed EC point: the y-coordinate, combined with the lowest bit (the parity) of the x-coordinate, making up 32 bytes. | ||
|
||
###### Example | ||
|
||
```ts | ||
const identityKey: Uint8Array = await wallet.keyGen(KeyContext.Identity, 0, 0); | ||
``` | ||
|
||
##### sign | ||
|
||
Signing operation that can be used to perform arbitrary signing operations in a given context. These can be for example: | ||
|
||
- Identity signatures (Documents, DIDs, etc) | ||
- Address signatures (i.e Signing auth challenges) | ||
|
||
The function should check if the data, before and after decoding, isn't a regular Algorand transaction. If it is, the function should throw an error. | ||
|
||
- **method name**: `sign` | ||
- **parameters**: | ||
- `data`: The data to be signed | ||
- `context`: The context of the key to be used for signing | ||
- `account`: The account index to be used for key derivation. The value value should be hardened as per BIP44 | ||
- `keyIndex`: The key index to be used for key derivation. | ||
- `metadata`: Object to describe the type of data that is being signed. It should specify the encoding used (i.e msgpack, cbor, etc) and a schema so that the wallet can decode it to show the user. | ||
- `encoding`: The encoding used for the data | ||
- `schema`: The schema of the data in the form of a JSON schema. The schemas are an powerful tool for wallet providers to be able to enforce the type and format of the data that is being signed. You can check schema examples <a href= "https://github.com/algorandfoundation/ARCs/tree/main/assets/arc-0052/schemas">here</a>. | ||
- **returns**: Byte array containing the signature. The signature is encoded as a 64-byte array (32 + 32 bytes). It holds a compressed point R + the integer s (confirming that the signer knows the msg and the privKey). | ||
- Throws error if data doesn't match the schema | ||
|
||
###### Example | ||
|
||
```ts | ||
const message = { | ||
letter: "Hello World", | ||
}; | ||
|
||
const encoded: Buffer = Buffer.from(to_base64(JSON.stringify(message))); | ||
|
||
// Schema of what we are signing | ||
const jsonSchema = { | ||
type: "object", | ||
properties: { | ||
letter: { | ||
type: "string", | ||
}, | ||
}, | ||
}; | ||
|
||
const metadata: SignMetadata = { | ||
encoding: Encoding.BASE64, | ||
schema: jsonSchema, | ||
}; | ||
|
||
const signature: Uint8Array = await cryptoService.signData( | ||
KeyContext.Address, | ||
0, | ||
0, | ||
encoded, | ||
metadata | ||
); | ||
expect(signature).toHaveLength(64); | ||
``` | ||
|
||
##### ECDH (Elliptic Curve Diffie-Hellman) | ||
|
||
This operation is used to perform a secret key aggrement between two parties. This is useful for example for deriving a shared symmetric secret that is only known between the participants. | ||
|
||
The symmetric secret can be used for encrypting and decrypting messages. | ||
|
||
- **method name**: `ECDH` | ||
- **parameters**: | ||
- `context`: The context of the key to be used for signing | ||
- `account`: The account index to be used for key derivation. The value value should be hardened as per BIP44 | ||
- `keyIndex`: The key index to be used for key derivation. | ||
- `otherPartyPub`: The public key of the other party | ||
- **returns**: Byte array containing the shared secret. The shared secret is a 32-byte array. | ||
|
||
###### Example | ||
|
||
```ts | ||
const aliceKey: Uint8Array = await cryptoService.keyGen( | ||
KeyContext.Address, | ||
0, | ||
0 | ||
); | ||
|
||
const bobKey: Uint8Array = await cryptoService.keyGen(KeyContext.Address, 0, 1); | ||
|
||
const aliceSharedSecret: Uint8Array = await cryptoService.ECDH( | ||
KeyContext.Address, | ||
0, | ||
0, | ||
bobKey | ||
); | ||
|
||
const bobSharedSecret: Uint8Array = await cryptoService.ECDH( | ||
KeyContext.Address, | ||
0, | ||
1, | ||
aliceKey | ||
); | ||
|
||
// Same secret can be used to encrypt by Alice and decrypt by Bob | ||
//and vice-versa | ||
expect(aliceSharedSecret).toEqual(bobSharedSecret); | ||
``` | ||
|
||
## Rationale | ||
|
||
At it's core, blockchain keys and signatures are just implementations of cryptographic primitives. These primitives can be used for a wide variety of use cases, not just for the purpose of signing transactions. And wallets being applications that contain or serve as Key Management Systems (KMS), should be able to support a multiple of other use cases that are not just related to transactions. This creates the possibility of attaching on-chain behavior with off-chain behavior, allowing for example to concepts of Identity and Authentication. | ||
|
||
The current wallet APIs available only support key derivation for addresses and signing for transactions. In the broader context of Algorand and web3 ecosystems, there is a need for a more flexible API that allows for contextual key generation and signing. An example of this could be identity dedicated keys (i.e DIDs), or passkeys for authentication. | ||
|
||
## Reference Implementation | ||
|
||
Reference Implementation is included in the `assets` folder <a href= "https://github.com/algorandfoundation/ARCs/tree/main/assets/arc-0052">here</a>. | ||
|
||
## Security Considerations | ||
|
||
As this functionality is based on EdDSA, BIP32 HD derivations and ECDH, the security considerations of those specifications apply. Particularly, the Ed25519 derived keys must be clamped to avoid known attacks | ||
ehanoc marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
## Copyright | ||
|
||
Copyright and related rights waived via <a href="https://creativecommons.org/publicdomain/zero/1.0/">CCO</a>. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
node_modules/ |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
# ARC52 reference implementation | ||
|
||
This implementation is not meant to be used in production. It is a reference implementation for the ARC52 specification. | ||
|
||
It shows how the ARC52 specification can be implemented on top of a BIP39 / BIP32-ed25519 / BIP44 wallet. | ||
|
||
note: Bip32-ed25519 is meant to be compatible with our Ledger implementation. Meaning that we only do hard-derivation at the level of the `account` as per BIP44 | ||
|
||
## Run | ||
|
||
```shell | ||
$ yarn | ||
$ yarn test | ||
``` | ||
|
||
## Output | ||
|
||
```shell | ||
PASS ./contextual.api.crypto.spec.ts | ||
Contextual Derivation & Signing | ||
✓ (OK) Root Key (2 ms) | ||
(JS Library) Reference Implementation alignment with known BIP32-Ed25519 JS LIB | ||
✓ (OK) BIP32-Ed25519 derive key m'/44'/283'/0'/0/0 (135 ms) | ||
✓ (OK) BIP32-Ed25519 derive key m'/44'/283'/0'/0/1 (120 ms) | ||
✓ (OK) BIP32-Ed25519 derive PUBLIC key m'/44'/283'/1'/0/1 (284 ms) | ||
✓ (OK) BIP32-Ed25519 derive PUBLIC key m'/44'/0'/1'/0/2 (277 ms) | ||
(Derivations) Context | ||
✓ (OK) ECDH (4 ms) | ||
✓ (OK) ECDH, Encrypt and Decrypt (5 ms) | ||
✓ Libsodium example ECDH (8 ms) | ||
Addresses | ||
Soft Derivations | ||
✓ (OK) Derive m'/44'/283'/0'/0/0 Algorand Address Key (1 ms) | ||
✓ (OK) Derive m'/44'/283'/0'/0/1 Algorand Address Key (1 ms) | ||
✓ (OK) Derive m'/44'/283'/0'/0/2 Algorand Address Key (2 ms) | ||
Hard Derivations | ||
✓ (OK) Derive m'/44'/283'/1'/0/0 Algorand Address Key (3 ms) | ||
✓ (OK) Derive m'/44'/283'/2'/0/1 Algorand Address Key (2 ms) | ||
✓ (OK) Derive m'/44'/283'/3'/0/0 Algorand Address Key (1 ms) | ||
Identities | ||
Soft Derivations | ||
✓ (OK) Derive m'/44'/0'/0'/0/0 Identity Key (1 ms) | ||
✓ (OK) Derive m'/44'/0'/0'/0/1 Identity Key (2 ms) | ||
✓ (OK) Derive m'/44'/0'/0'/0/2 Identity Key (1 ms) | ||
Hard Derivations | ||
✓ (OK) Derive m'/44'/0'/1'/0/0 Identity Key (2 ms) | ||
✓ (OK) Derive m'/44'/0'/2'/0/1 Identity Key (1 ms) | ||
Signing Typed Data | ||
✓ (OK) Sign Arbitrary Message against Schem (54 ms) | ||
✓ (FAIL) Signing attempt fails because of invalid data against Schema (33 ms) | ||
Reject Regular Transaction Signing. IF TAG Prexies are present signing must fail | ||
✓ (FAIL) [TX] Tag | ||
✓ (FAIL) [MX] Tag (1 ms) | ||
✓ (FAIL) [Program] Tag | ||
✓ (FAIL) [progData] Tag (1 ms) | ||
Reject tags present in the encoded payload | ||
✓ (FAIL) [TX] Tag (2 ms) | ||
✓ (FAIL) [MX] Tag | ||
✓ (FAIL) [Program] Tag (1 ms) | ||
✓ (FAIL) [progData] Tag | ||
|
||
|
||
``` | ||
|
||
## BIP39 / BIP32-ed25519 / BIP44 Test Vectors | ||
|
||
All keys are in extended format: [kl][kr][chaincode] | ||
|
||
Public key = kl * Ed25519GeneratorPoint | ||
|
||
- `BIP39 mnemonic`: _salon zoo engage submit smile frost later decide wing sight chaos renew lizard rely canal coral scene hobby scare step bus leaf tobacco slice_ | ||
|
||
- `root key (hex)`: a8ba80028922d9fcfa055c78aede55b5c575bcd8d5a53168edf45f36d9ec8f4694592b4bc892907583e22669ecdf1b0409a9f3bd5549f2dd751b51360909cd05b4b67277d74d4ddb3688daeeb02075482ceb812db8a5757c9e792d14ec791554 | ||
|
||
### BIP44 paths | ||
|
||
#### Child Private Derivation | ||
|
||
- `m'/44'/283'/0'/0/0`: 70db1fe0dd722955f44b57b26eee09ac282172559eb2bc6b408f69e2e9ec8f461b8fbc75f0e2a9f454d7de35a812c97fa08b984df342389f4d1f9aec5637e67468c11d826d16f7f505d27bd52c314caea79a7b9b6e87f5aa7e20f6cf06ac51f9 | ||
- corresponding public key: 7915e7ecbaad1dc9bc22a9e496686687f1a8cb4895b7ca46f86d64dd56c6cd97 (kl * Ed25519GeneratorPoint) | ||
|
||
- `m'/44'/283'/0'/0/1`: 58678408f4f88d7ec700be1090940bedd584c21dc7f8b00028b69e01edec8f4645779d1c90ecc5168945024e4201552a349a825518badb8d4b017bf1dcce4dac910cb8dbcd10680c7754ca101089f4c9da61a2dff79e0301d9a1fb7656fb8c73 | ||
- public key: 054a6881d8809c348a402d67ba2feedcd8e3145f40f21a6bbd0de09c30c78d0a | ||
|
||
|
||
- `m'/44'/283'/0'/0/2`: 88ce207945dfecea41acbb2a9b4563268d3bee03ea2e199af502c500e7ec8f467b95d890e8c7e5aa79d9905f3c6794e1fc3bb9478552b7f24c751b94dd3becd76699130de29273b2ae742991b7daa0adae6fb656038b25895d4af3c85769a64d | ||
- public key: 8cea8052cfa1fd8cec0b4fad6241a91f2edbfe9f072586f243839174e40a25ef | ||
|
||
- `m'/44'/283'/1'/0/0`: 18ada9291ae27f7415e99f4485d81493e68952c8b1af4f335cc52962ecec8f464c106ef1f56fa64b2a13631a68a6d6c9902eec52e2c6226a3ed953ae94bc8a613303bbf403f94b5eb189eed703b3985e12c6726d6b06d4ed8aac3d5b258e3c8c | ||
- public key: 04f3ba279aa781ab4f8f79aaf6cf91e3d7ff75429064dc757001a30e10c628df | ||
|
||
- `m'/44'/283'/2'/0/0`: 209dc0b3458ee1f7484189bf584c02807f1a5726168aff5ea14fc117f3ec8f46b68ea14ca84c0da34aa4990416c6da01b14fbe30c5c225238515a93a0a28a33dbecbe8e64a514d44c8c0a3775844d5aa8e18ea2182321f1ff87061e49adc4a77 | ||
- public key: 400d78302258dc7b3cb56d1a09f85b018e8100865ced0b5cda474c26bbc07c30 | ||
|
||
|
||
- `m'/44'/283'/3'/0/0`: 3008165b92e6b29d6a3f28b593f15dcf6ce4c9a5a1aadc3a43ca068ce7ec8f46b8251db83f68dedaee1054ec545257f95ba00e33566d99926bef8743b77b42b8867472d1f1886c88ec36991f14a333454003236b5375d4a8bf4f01b2ff85ec9d | ||
- public key: cf8d28a3d41bc656acbfeadb64d06054142c97bee6a987c11d934f84853df866 | ||
|
||
- `m'/44'/0'/0'/0/0`: 08b10dfcfc37a0cc998ec3305c6c32d4412de73f3f9633342248d14aeeec8f461997b1136524a5d3499d933c6f6e739d38fbf7404a183f4d835c6fe105cf44d016634694e4087b2d547c8b550a0f053d2fba990ba7e5e58186963393994d06a9 | ||
- public key: 28804f08d8c145e172c998fe75058237b8181846ca763894ae3eefea6ab88352 | ||
|
||
- `m'/44'/0'/0'/0/1`: 80d6dd1cefb42fc93187739ad102e5dfd533b54b847b0693661bfee9edec8f466cab918344a8e7e9ae196d94cce301aa1d7360f550b25f3dc7468a006e423e3de80e43cd2588a9996b8156d3dc233ca31470f7d49261edd2e13d8c4c0096163c | ||
- public key: fc8d1c79edd406fa415cb0a76435eb83b6f8af72cd9bd673753471470205057a | ||
|
||
- `m'/44'/0'/0'/0/2`: 7847139bee38330c809a56f3d2261bfbd7da7c1c88999c38bb12175cefec8f467617b7dba8330b644eae1c24a2d6b7211f5f36d9919d9a240777fa32d382feb3debfe21afa0de5021342f3bfafe18a91e11c441ab98ff5bcbba2dfba3190ce6f | ||
- public key: f7e317899420454886fe79f24e25af0bbb1856c440b14829674015e5fc2ad28a | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A few points here:
purpose
44 equal to BIP-44? IMO we should use a differentpurpose
from Bitcoin's wallets.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey Stefano,
So, regarding
We have discussed this before. It doesn't really matter that is the same path as in bitcoin, it's 2 different elliptic curves. And this is following the spec for the W3C Universal Wallet API(https://github.com/w3c-ccg/universal-wallet-interop-spec)
Yea, we could extend it in the future to other ecosystems / applications
The discovery algorithm should be the same as any other chain wallet. Recursive finding until no account / address has any activity. For off-chain use, if you need to recovery, all is still recoverable, depending on the context you need to loop through them and see if you can use them, or can include them in wallet backups.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
purpose
as a derivation path. For example, SLIP-0013 provides authentication for HD wallets and usespurpose = 13'
for authentication with deterministic hierarchy.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand the point about assigning the coin type to different ASAs. Given that Algo is needed to opt in and transact with an ASA regardless, how would assigning ASA IDs to coin type work? And what if you want/need to hold multiple ASAs in one address?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What I like about having different coin types for ASAs is that it forces users to split their holdings into different addresses. This would improve security in case of compromised keys.
Maybe changing the coin type is not the best approach because of ALGO though.
As you mentioned, ALGO opt-in is a protocol requirement as well as fees, so any address should be intended to hold ALGOs with the ASA. However, we could think of another derivation level dedicated to ASAs. Some related discussion here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel that the biggest risk here is that the seed gets compromised, in which case all addresses are compromised regardless of the derivation path. I think it is unlikely that a single derived private key (i.e. a scalar output of the deriveKey function) could be compromised without the seed or many addresses also being compromised in the process.
If we want users want to split their funds wallets can encourage them and make it easy to use different indices or different accounts for accepting different tokens.