Skip to content
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

Ensip 16 with write support #169

Draft
wants to merge 5 commits into
base: master
Choose a base branch
from
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 47 additions & 10 deletions ens-improvement-proposals/ensip-16-offchain-metadata.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ This ENSIP addresses this by adding a way of important metadata to be gathered o

The metadata should include 2 different types of info

- Offchain data storage location related info: `graphqlUrl` includes the URL to fetch the metadata.
- Offchain data storage location related info: `graphqlUrl` includes the URL to fetch the metadata.

- Ownership related info: `owner`, `isApprovedForAll` defines who can own or update the given record.

Expand All @@ -47,23 +47,17 @@ By passing the name through metadata, we empower the resolution process, enablin
// To be included in
// https://github.com/ensdomains/ens-contracts/blob/staging/contracts/resolvers/Resolver.sol
interface IOffChainResolver {
/** @dev Returns the owner of the resolver on L2
* @param node
* @return owner in bytes32 instead of address to cater for non EVM based owner information
*/
owner(bytes32 node) returns (bytes owner);

// optional.
// this returns data via l2 with EIP-3668 so that non EVM chains can also return information of which address can update the record
// The same function name exists on L2 where delegate returns address instead of bytes
function isApprovedFor(bytes context, bytes32 node, bytes delegate) returns (bool);

/** @dev Returns the owner of the resolver on L2
/** @dev Returns the metadata about the node
* @return name can be l2 chain name or url if offchain
* @return coinType according to https://github.com/ensdomains/address-encoder
* @return graphqlUrl url of graphql endpoint that provides additional information about the offchain name and its subdomains
* @return storageType 0 = EVM, 1 = Non blockchain, 2 = Starknet
* @storageLocation = l2 contract address
* @storageLocation = When storageType is 0, l2 contract address. When storageType is 1, url endpoint in bytes format
* @return context = an arbitrary bytes string to define the namespace to which a record belongs such as the name owner.
*/
function metadata(bytes calldata name)
Expand Down Expand Up @@ -190,6 +184,49 @@ type Resolver @entity {
}
```

### Offchain storage service

[EIP-5559](https://eips.ethereum.org/EIPS/eip-5559#data-stored-in-an-off-chain-database) has already been proposed to handle both L2 and off chain storage by utilizing the similar method to [EIP-3668](https://eips.ethereum.org/EIPS/eip-3668).
While this is technically feasible, it adds extra function calls simply to discover how to update the data. The simpler alternative for ENS related storage update is to query storage service endpoint from `storageLocation` in bytes format when `storateType` is set to `1`. The client then make a HTTP POST call to the storage location with signature information.

The body attached to the request is a JSON object that includes sender, data, inception date and a signed copy of the abi encoded data, sender, and inception date.

Example HTTP POST request including requestParams and signature:

```
const keccak256 = ethers.utils.solidityKeccak256
const name = 'vitalik.eth'
const node = ethers.utils.namehash(name)
const iface = new ethers.utils.Interface(['function setAddr(bytes,address)'])
const signer = signers[0]
const signerAddress = signer.address
const data = iface.encodeFunctionData('setAddr',[node, signerAddress])
const hasheddata = keccak256(['bytes'], [data])
const block = await ethers.provider.getBlock('latest')
inception = block.timestamp
signature = await signers[0].signMessage(
ethers.utils.arrayify(
keccak256(
['bytes', 'uint256'],
[
hasheddata,
inception
],
),
),
)
```

```
const request = {
data:,
inception,
signature
}
```

Once the storage service receives the request, it decodes the sender information from the signed signature and perform the data update if the `sender` matches the name onwer defined by the optional `context` field.

### Backwards Compatibility

None
Expand All @@ -200,4 +237,4 @@ None

### Copyright

Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).
Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).