Skip to content

Commit

Permalink
410 resolver addr (#411)
Browse files Browse the repository at this point in the history
* remove nameprep

* encode generic response instread of typed userProfile

* decodeCalldata based on sig

* handle request based on sig

* add decode addr

* resolve the address of a given name

* clean up ccip Gateway

* reame decodeCalldata to decodeRequest

* change port

* switch port
  • Loading branch information
AlexNi245 authored Jan 19, 2023
1 parent f05e14e commit 0be4ba6
Show file tree
Hide file tree
Showing 23 changed files with 447 additions and 277 deletions.
21 changes: 21 additions & 0 deletions packages/lib/src/offchainResolver/encoding/decode/decodeAddr.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { ethers } from 'ethers';
import { decodeDnsName } from '../../dns/decodeDnsName';

/**
Decodes the call data of addr(bytes 32)
@param ensName - The ENS name to be decoded.
@param data - The data containing the namehash.
@returns An object containing the name.
@throws An error if the namehash doesn't match the ENS name.
*/
export function decodeAddr(ensName: string, data: ethers.utils.Result) {
const [nameHash] = data;

const name = decodeDnsName(ensName);

if (ethers.utils.namehash(name) !== nameHash) {
throw Error("Namehash doesn't match");
}

return { name };
}
21 changes: 21 additions & 0 deletions packages/lib/src/offchainResolver/encoding/decode/decodeText.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { ethers } from 'ethers';
import { decodeDnsName } from '../../dns/decodeDnsName';

/**
Decodes the text record of a given ENS name and returns an object containing the name and the record.
@param ensName - The ENS name to be decoded.
@param data - The data containing the namehash and the record.
@returns An object containing the name and the record.
@throws An error if the namehash doesn't match the ENS name.
*/
export function decodeText(ensName: string, data: ethers.utils.Result) {
const [nameHash, record] = data;

const name = decodeDnsName(ensName);

if (ethers.utils.namehash(name) !== nameHash) {
throw Error("Namehash doesn't match");
}

return { name, record };
}
39 changes: 0 additions & 39 deletions packages/lib/src/offchainResolver/encoding/decodeCalldata.test.ts

This file was deleted.

45 changes: 0 additions & 45 deletions packages/lib/src/offchainResolver/encoding/decodeCalldata.ts

This file was deleted.

68 changes: 68 additions & 0 deletions packages/lib/src/offchainResolver/encoding/decodeRequest.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { ethers } from 'ethers';
import { decodeRequest } from './decodeRequest';
import { getResolverInterface } from './getResolverInterface';
import { encodeEnsName } from '../dns/encodeEnsName';

describe('decodeRequest', () => {
describe('decodeText', () => {
it('decodes valid calldata', () => {
const textData = getResolverInterface().encodeFunctionData('text', [
ethers.utils.namehash('foo.dm3.eth'),
'dm3.profile',
]);

const calldata = getResolverInterface().encodeFunctionData(
'resolve',
[encodeEnsName('foo.dm3.eth'), textData],
);
const { request } = decodeRequest(calldata);

expect(request.record).toBe('dm3.profile');
expect(request.name).toBe('foo.dm3.eth');
});

it('throws if namehash does not matched encoded ens.name', () => {
const textData = getResolverInterface().encodeFunctionData('text', [
ethers.utils.namehash('FOOO'),
'dm3.profile',
]);

const calldata = getResolverInterface().encodeFunctionData(
'resolve',
[encodeEnsName('foo.dm3.eth'), textData],
);

expect(() => decodeRequest(calldata)).toThrowError(
"Namehash doesn't match",
);
});
});
describe('decodeAddr', () => {
it('decodes valid calldata', () => {
const textData = getResolverInterface().encodeFunctionData('addr', [
ethers.utils.namehash('foo.dm3.eth'),
]);

const calldata = getResolverInterface().encodeFunctionData(
'resolve',
[encodeEnsName('foo.dm3.eth'), textData],
);
const { request } = decodeRequest(calldata);
expect(request.name).toBe('foo.dm3.eth');
});
it('throws if namehash does not matched encoded ens.name', () => {
const textData = getResolverInterface().encodeFunctionData('addr', [
ethers.utils.namehash('FOOO'),
]);

const calldata = getResolverInterface().encodeFunctionData(
'resolve',
[encodeEnsName('foo.dm3.eth'), textData],
);

expect(() => decodeRequest(calldata)).toThrowError(
"Namehash doesn't match",
);
});
});
});
39 changes: 39 additions & 0 deletions packages/lib/src/offchainResolver/encoding/decodeRequest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { log } from '../../shared/log';
import { DecodedCcipRequest } from '../types';
import { decodeAddr } from './decode/decodeAddr';
import { decodeText } from './decode/decodeText';
import { getResolverInterface } from './getResolverInterface';

/**
Decodes a given calldata string and returns a DecodedCcipRequest object containing the signature and request.
@param calldata - The calldata string to be decoded.
@returns A {@see DecodedCcipRequest} object containing the signature and request.
@throws An error if the calldata cannot be decoded or if the signature is not supported.
*/
export function decodeRequest(calldata: string): DecodedCcipRequest {
try {
const textResolver = getResolverInterface();

//Parse the calldata returned by a contra
const [ensName, data] = textResolver.parseTransaction({
data: calldata,
}).args;

const { signature, args } = textResolver.parseTransaction({
data,
});

switch (signature) {
case 'text(bytes32,string)':
return { signature, request: decodeText(ensName, args) };
case 'addr(bytes32)':
return { signature, request: decodeAddr(ensName, args) };
default:
throw Error(`${signature} is not supported`);
}
} catch (err: any) {
log("[Decode Calldata] Can't decode calldata ");
log(err);
throw err;
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { ethers } from 'ethers';
import { UserProfile } from '../../account';
import { encodeEnsName } from '../dns/encodeEnsName';
import { encodeUserProfile } from './encodeUserProfile';
import { encodeResponse } from './encodeResponse';
import { getResolverInterface } from './getResolverInterface';

describe('encodeUserProfie', () => {
describe('encodeResponse', () => {
it('encodes userProfile properly', async () => {
const signer = ethers.Wallet.createRandom();
const profile: UserProfile = {
const response: UserProfile = {
publicSigningKey: '0ekgI3CBw2iXNXudRdBQHiOaMpG9bvq9Jse26dButug=',
publicEncryptionKey: 'Vrd/eTAk/jZb/w5L408yDjOO5upNFDGdt0lyWRjfBEk=',
deliveryServices: [''],
Expand All @@ -25,10 +25,10 @@ describe('encodeUserProfie', () => {

const functionSelector = 'text(bytes32,string)';

const encodedProfile = await encodeUserProfile(
const encodedProfile = await encodeResponse(
signer,
profile,
signer.address,
response,
calldata,
functionSelector,
);
Expand All @@ -42,6 +42,6 @@ describe('encodeUserProfie', () => {
'text',
encodedResult,
);
expect(JSON.parse(decodedProfile)).toStrictEqual(profile);
expect(JSON.parse(decodedProfile)).toStrictEqual(response);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ import { getResolverInterface } from './getResolverInterface';
* @param ttl the time to life to calculate validUntil.
* @returns the encoded response
*/
export async function encodeUserProfile(
export async function encodeResponse(
signer: Signer,
userProfile: UserProfile,
resolverAddr: string,
response: any,
request: string,
functionSelector: string,
ttl: number = 300,
Expand All @@ -25,7 +25,7 @@ export async function encodeUserProfile(
const validUntil = Math.floor(Date.now() / 1000 + ttl);

const result = textResolver.encodeFunctionResult(functionSelector, [
stringify(userProfile),
stringify(response),
]);
/**
* This hash has to be compiled the same way as at the OffchainResolver.makeSignatureHash method
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@ export function getResolverInterface() {
'function text(bytes32 node, string calldata key) external view returns (string memory)',
// eslint-disable-next-line max-len
'function resolveWithProof(bytes calldata response, bytes calldata extraData) external view returns (bytes memory)',
'function addr(bytes32 node) external view returns (address)',
]);
}
4 changes: 2 additions & 2 deletions packages/lib/src/offchainResolver/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export { decodeCalldata } from './encoding/decodeCalldata';
export { encodeUserProfile } from './encoding/encodeUserProfile';
export { decodeRequest } from './encoding/decodeRequest';
export { encodeResponse } from './encoding/encodeResponse';
export { decodeDnsName } from './dns/decodeDnsName';
export { encodeEnsName } from './dns/encodeEnsName';

Expand Down
6 changes: 2 additions & 4 deletions packages/lib/src/offchainResolver/types.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
/**
* @param name the ENS name after after decoding
* @param record the name of the record that should be queried
* @param request the decoded function params of the request. i.E {name:string,record:string}
* @param signature the signature of the function the request should query
*/
export interface DecodedCcipRequest {
name: string;
record: string;
request: any;
signature: string;
}
Loading

0 comments on commit 0be4ba6

Please sign in to comment.