Skip to content

Commit

Permalink
Merge pull request #29 from starknet-id/feat/add_ccip
Browse files Browse the repository at this point in the history
feat: update getAddressFromStarkName to work with ccip
  • Loading branch information
Th0rgal authored Apr 23, 2024
2 parents 8754a90 + 380a2a8 commit 5d57e04
Show file tree
Hide file tree
Showing 8 changed files with 10,283 additions and 18 deletions.
6,883 changes: 6,883 additions & 0 deletions packages/core/__mocks__/resolver/resolver.compiled_contract_class.json

Large diffs are not rendered by default.

3,057 changes: 3,057 additions & 0 deletions packages/core/__mocks__/resolver/resolver.contract_class.json

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions packages/core/__test__/fixtures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ export const compiledUtilsMulticallSierraCasm = readContractSierraCasm(
);
export const compiledErc721Sierra = readContractSierra("erc721/erc721");
export const compiledErc721SierraCasm = readContractSierraCasm("erc721/erc721");
export const compiledResolverSierra = readContractSierra("resolver/resolver");
export const compiledResolverSierraCasm =
readContractSierraCasm("resolver/resolver");

/* Default test config based on run `starknet-devnet --seed 0` */
const DEFAULT_TEST_RPC_URL = "http://127.0.0.1:5050/";
Expand Down
11 changes: 11 additions & 0 deletions packages/core/__test__/profile.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
} from "../src/utils";

jest.mock("../src/utils");
global.fetch = jest.fn();

describe("test starknetid.js sdk", () => {
jest.setTimeout(90000000);
Expand Down Expand Up @@ -263,6 +264,10 @@ describe("test starknetid.js sdk", () => {
});

describe("getProfileData with nft profile picture", () => {
beforeEach(() => {
fetch.mockClear();
});

beforeAll(async () => {
// Add nft pp verifier data
const { transaction_hash } = await otherAccount.execute(
Expand Down Expand Up @@ -297,6 +302,12 @@ describe("test starknetid.js sdk", () => {
});

test("getProfileData should return the right values", async () => {
fetch.mockResolvedValue({
ok: true,
json: async () => ({
image: "https://sepolia.starknet.quest/starkfighter/level1.webp",
}),
});
const starknetIdNavigator = new StarknetIdNavigator(
provider,
constants.StarknetChainId.SN_GOERLI,
Expand Down
192 changes: 192 additions & 0 deletions packages/core/__test__/resolve.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
import { Account, constants } from "starknet";
import { StarknetIdNavigator } from "../src";
import {
compiledIdentitySierra,
compiledIdentitySierraCasm,
compiledNamingSierra,
compiledNamingSierraCasm,
compiledPricingSierra,
compiledPricingSierraCasm,
compiledResolverSierra,
compiledResolverSierraCasm,
getTestAccount,
getTestProvider,
} from "./fixtures";

global.fetch = jest.fn();

describe("test starknetid.js sdk", () => {
jest.setTimeout(90000000);
const provider = getTestProvider();
const account = getTestAccount(provider)[0];

let erc20Address: string =
"0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7";
let NamingContract: string;
let IdentityContract: string;
let ResolverContract: string;

beforeAll(async () => {
expect(account).toBeInstanceOf(Account);

// Deploy Identity contract
const idResponse = await account.declareAndDeploy(
{
contract: compiledIdentitySierra,
casm: compiledIdentitySierraCasm,
constructorCalldata: [account.address, 0],
},
{ maxFee: 1e18 },
);
IdentityContract = idResponse.deploy.contract_address;

// Deploy pricing contract
const pricingResponse = await account.declareAndDeploy(
{
contract: compiledPricingSierra,
casm: compiledPricingSierraCasm,
constructorCalldata: [erc20Address],
},
{ maxFee: 1e18 },
);
const pricingContractAddress = pricingResponse.deploy.contract_address;

// Deploy naming contract
const namingResponse = await account.declareAndDeploy(
{
contract: compiledNamingSierra,
casm: compiledNamingSierraCasm,
constructorCalldata: [
IdentityContract,
pricingContractAddress,
0,
account.address,
],
},
{ maxFee: 1e18 },
);
NamingContract = namingResponse.deploy.contract_address;

// Deploy resolver contract
const publicKey =
"0x1ef1ffcd39066b79fd741ed17c8bed5fab0160591d8b7177211f5e7f5517a04";
const serverUri = ["http://0.0.0.0:8090/resolve?do", "main="];
const resolverResponse = await account.declareAndDeploy(
{
contract: compiledResolverSierra,
casm: compiledResolverSierraCasm,
constructorCalldata: [account.address, publicKey],
},
{ maxFee: 1e18 },
);
ResolverContract = resolverResponse.deploy.contract_address;

const { transaction_hash } = await account.execute(
[
{
contractAddress: erc20Address,
entrypoint: "approve",
calldata: [NamingContract, 0, 1], // Price of domain
},
{
contractAddress: IdentityContract,
entrypoint: "mint",
calldata: ["1"], // TokenId
},
{
contractAddress: NamingContract,
entrypoint: "buy",
calldata: [
"1", // Starknet id linked
"1068731", // Domain encoded "test"
"365", // days
ResolverContract, // resolver
0, // sponsor
0,
0,
],
},
{
contractAddress: IdentityContract,
entrypoint: "set_main_id",
calldata: ["1"],
},
{
contractAddress: ResolverContract,
entrypoint: "add_uri",
calldata: [2, ...serverUri],
},
],
undefined,
{ maxFee: 1e18 },
);
await provider.waitForTransaction(transaction_hash);
});

describe("resolve domain", () => {
beforeEach(() => {
fetch.mockClear();
});

test("resolve subdomain returns the right address", async () => {
fetch.mockResolvedValue({
ok: true,
json: async () => ({
address:
"0x64b48806902a367c8598f4f95c305e8c1a1acba5f082d294a43793113115691",
r: "0x7bdc9f102e7085464431ae1a89f1d1cc51abf0a1dfa3fba8016b05cb4365219",
s: "0x6d557890203c75df13d880691ac8af5323d0cb7c944d34fc271425f442eae9f",
max_validity: 1716966719,
}),
});
const starknetIdNavigator = new StarknetIdNavigator(
provider,
constants.StarknetChainId.SN_GOERLI,
{
naming: NamingContract,
identity: IdentityContract,
},
);
expect(starknetIdNavigator).toBeInstanceOf(StarknetIdNavigator);
const address = await starknetIdNavigator.getAddressFromStarkName(
"iris.test.stark",
);
expect(address).toBe(account.address);
});

test("resolve root domain returns the right address", async () => {
const starknetIdNavigator = new StarknetIdNavigator(
provider,
constants.StarknetChainId.SN_GOERLI,
{
naming: NamingContract,
identity: IdentityContract,
},
);
expect(starknetIdNavigator).toBeInstanceOf(StarknetIdNavigator);
const address = await starknetIdNavigator.getAddressFromStarkName(
"test.stark",
);
expect(address).toBe(account.address);
});

test("resolve subdomain that is not registered returns an error", async () => {
fetch.mockResolvedValue({
ok: false,
json: async () => ({ error: "Domain not found" }),
});
const starknetIdNavigator = new StarknetIdNavigator(
provider,
constants.StarknetChainId.SN_GOERLI,
{
naming: NamingContract,
identity: IdentityContract,
},
);
expect(starknetIdNavigator).toBeInstanceOf(StarknetIdNavigator);
await expect(
starknetIdNavigator.getAddressFromStarkName("notworking.test.stark"),
).rejects.toThrow("Could not get address from stark name");
});
});
});
69 changes: 51 additions & 18 deletions packages/core/src/starknetIdNavigator/default.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@ import {
getChecksumAddress,
constants,
CallData,
hash,
Contract,
RawArgsArray,
Call,
} from "starknet";
import {
Expand All @@ -27,18 +25,15 @@ import {
import { StarknetIdNavigatorInterface } from "./interface";
import { StarkProfile, StarknetIdContracts } from "../types";
import {
arrayReference,
executeMulticallWithFallback,
executeWithFallback,
extractArrayFromErrorMessage,
fetchImageUrl,
getProfileDataCalldata,
getStarkProfilesCalldata,
getStarknamesCalldata,
hardcoded,
notEqual,
parseBase64Image,
reference,
staticExecution,
queryServer,
} from "./internal";

export class StarknetIdNavigator implements StarknetIdNavigatorInterface {
Expand All @@ -62,19 +57,44 @@ export class StarknetIdNavigator implements StarknetIdNavigatorInterface {
public async getAddressFromStarkName(domain: string): Promise<string> {
const contract =
this.StarknetIdContract.naming ?? getNamingContract(this.chainId);
const encodedDomain = encodeDomain(domain).map((elem) => elem.toString(10));

try {
const encodedDomain = encodeDomain(domain).map((elem) =>
elem.toString(10),
);
const addressData = await this.provider.callContract({
contractAddress: contract,
entrypoint: "domain_to_address",
calldata: CallData.compile({ domain: encodedDomain, hint: [] }),
});
return addressData.result[0];
} catch {
throw new Error("Could not get address from stark name");
return await this.tryResolveDomain(contract, encodedDomain, []);
} catch (error) {
if (error instanceof Error) {
// extract server uri from error message
const data = extractArrayFromErrorMessage(String(error));
if (!data || data?.errorType !== "offchain_resolving") {
// if the error is not related to offchain resolving
throw new Error("Could not get address from stark name");
}

// we try querying the server for each uri, and will stop once one is working
for (const uri of data.uris) {
try {
const serverRes = await queryServer(uri, data.domain_slice);
if (serverRes.error) {
continue;
}
// try resolving with hint
const hint: any[] = [
serverRes.data.address,
serverRes.data.r,
serverRes.data.s,
serverRes.data.max_validity,
];
return await this.tryResolveDomain(contract, encodedDomain, hint);
} catch (error: any) {
throw new Error(
`Could not resolve domain on URI ${uri} : ${error.message}`,
);
}
}
throw new Error("Could not get address from stark name");
} else {
throw new Error("Could not get address from stark name");
}
}
}

Expand Down Expand Up @@ -632,6 +652,19 @@ export class StarknetIdNavigator implements StarknetIdNavigatorInterface {
}
}

private async tryResolveDomain(
contract: string,
encodedDomain: string[],
hint: any = [],
): Promise<string> {
const addressData = await this.provider.callContract({
contractAddress: contract,
entrypoint: "domain_to_address",
calldata: CallData.compile({ domain: encodedDomain, hint }),
});
return addressData.result[0];
}

private async checkArguments(idDomainOrAddr: string): Promise<string> {
if (typeof idDomainOrAddr === "string") {
if (/^\d+$/.test(idDomainOrAddr)) {
Expand Down
Loading

0 comments on commit 5d57e04

Please sign in to comment.