diff --git a/docs/ics/ics-xxx-signed-messages.md b/docs/ics/ics-xxx-signed-messages.md
new file mode 100644
index 000000000..e7d4e1a58
--- /dev/null
+++ b/docs/ics/ics-xxx-signed-messages.md
@@ -0,0 +1,144 @@
+# ICS XXX: Cosmos Signed Messages
+
+>TODO: Replace with valid ICS number and possibly move to new location.
+
+## Changelog
+
+## Abstract
+
+Having the ability to sign messages off-chain has proven to be a fundamental aspect
+of nearly any blockchain. The notion of signing messages off-chain has many
+added benefits such as saving on computational costs and reducing transaction
+throughput and overhead. Within the context of the Cosmos, some of the major
+applications of signing such data includes, but is not limited to, providing a
+cryptographic secure and verifiable means of proving validator identity and
+possibly associating it with some other framework or organization. In addition,
+having the ability to sign Cosmos messages with a Ledger or similar HSM device.
+
+A standardized protocol for hashing, signing, and verifying messages that can be
+implemented by the Cosmos SDK and other third-party organizations is needed. Such a
+standardized protocol subscribes to the following:
+
+* Contains a specification of machine-verifiable and human-readable typed structured data
+* Contains a framework for deterministic and injective encoding of structured data
+* Utilizes cryptographic secure hashing and signing algorithms
+* A framework for supporting extensions and domain separation
+* Is invulnerable to chosen ciphertext attacks
+* Has protection against potentially signing transactions a user did not intend to
+
+This specification is only concerned with the rationale and the standardized
+implementation of Cosmos signed messages. It does **not** concern itself with the
+concept of replay attacks as that will be left up to the higher-level application
+implementation. If you view signed messages in the means of authorizing some
+action or data, then such an application would have to either treat this as
+idempotent or have mechanisms in place to reject known signed messages.
+
+## Specification
+
+> The proposed implementation is motivated and borrows heavily from EIP-7121
+and in general Ethereum's `eth_sign` and `eth_signTypedData` methods2.
+
+### Preliminary
+
+We will a have Cosmos SDK message signing protocol that consists of using `SHA-256`,
+as the hashing algorithm and `secp256k1` as the signing algorithm.
+
+Note, our goal here is not to provide context and reasoning about why necessarily
+these algorithms were chosen apart from the fact they are the defacto algorithms
+used in Tendermint and the Cosmos SDK and that they satisfy our needs for such
+cryptographic algorithms such as having resistance to collision and second
+pre-image attacks, as well as being deterministic and uniform.
+
+### Encoding
+
+Our goal is to create a deterministic, injective, machine-verifiable means of
+encoding human-readable typed and structured data.
+
+Let us consider the set of signed messages to be: `B ∪ S`, where `B` is the set
+of byte strings and `S` is the set of human-readable typed structures. Thus, the
+set can can be encoded in a deterministic and injective way via the following
+rules, where `||` denotes concatenation:
+
+* `encode(b : B)` = `p1 || bytes("Signed Cosmos SDK Message: \n") || l || b`, where
+ * `l`: little endian uint64 encoding of the length of `b`
+ * `p1`: prefix to distinguish from normal transactions and other encoding cases
+* `encode(s : S, domainSeparator : B)` = `p2 || domainSeparator || amino(s)`, where
+ * `domainSeparator`: 32 byte encoding of the domain separator [see below](###DomainSeparator)
+ * `p2`: prefix to distinguish from normal transactions and other encoding cases
+
+> TODO: Figure out byte(s) prefix in the encoding to not have collisions with
+typical transaction signatures (JSON-encoded) and to distinguish the individual
+cases. This may require introducing prefixes to transactions.
+
+#### Assumptions
+
+`amino` is deterministic and seems to be injective -- at least when when
+concrete types are registered. Can we rely on this mechanism or do we need a
+custom true injective encoding?
+
+> TODO: Do we need to implement a custom encoding scheme instead of or atop
+amino similar or equal to EIP-712 `hashStruct`? As far as I know, this is mostly
+due to providing Soldity efficiencies and addressing limitations.
+
+### DomainSeparator
+
+Encoding structures can still lead to potential collisions and while this may be
+ok or even desired, it introduces a concern in that it could lead to two compatible
+signatures. The domain separator prevents collisions of otherwise identical
+structures. It is designed to unique per application use and is directly used in
+the signature encoding itself. The domain separator is also extensible where the
+protocol and application designer may introduce or omit fields to their liking,
+but we will provide a typical structure that can be used for proper separation
+of concerns:
+
+```golang
+type DomainSeparator struct {
+ name string // A user readable name of the signing origin or application.
+ chainID string // The corresponding Cosmos chain identifier.
+ version uint16 // Version of the domain separator. A single major version should suffice.
+ salt []byte // Random data to further provide disambiguation.
+}
+```
+
+Application designers may choose to omit or introduce additional fields to a
+domain separator. However, users should provided with the exact information for
+which they will be signing (i.e. a user should always know the chain ID they are
+signing for).
+
+Given the set of all domain separators, the encoding of the domain separator
+is as follows:
+
+* `encode(domainSeparator : B)` = `sha256(amino(domainSeparator))`
+
+## API
+
+We now formalize a standard set of APIs that providers must implement:
+
+```
+cosmosSignBytes(b : B, addr : B) {
+ return secp256k1Sign(sha256(encode(b)), addressPrivKey(addr))
+}
+```
+
+```
+cosmosSignBytesPass(b : B, pass : B) {
+ return secp256k1Sign(sha256(encode(b)), passPrivKey(addr))
+}
+```
+
+```
+cosmosSignTyped(s : S, domainSeparator : B, addr : B) {
+ return secp256k1Sign(sha256(encode(s, domainSeparator)), addressPrivKey(addr))
+}
+```
+
+```
+cosmosSignTypedPass(s : S, domainSeparator : B, pass : B) {
+ return secp256k1Sign(sha256(encode(s, domainSeparator)), passPrivKey(addr))
+}
+```
+
+## References
+
+1. https://github.com/ethereum/EIPs/blob/master/EIPS/eip-712.md
+2. https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sign