-
Notifications
You must be signed in to change notification settings - Fork 1.3k
feat(BA-2133): ENSIP-19 updates for registration flow for EOAs #2594
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
Changes from 7 commits
614d8ea
ee8e736
bf7ae22
3e8c3a6
7d3ebd7
9b1d482
9ddefb3
a1ad726
9824676
44109eb
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 |
|---|---|---|
|
|
@@ -19,12 +19,15 @@ import { | |
| normalizeEnsDomainName, | ||
| REGISTER_CONTRACT_ABI, | ||
| REGISTER_CONTRACT_ADDRESSES, | ||
| buildReverseRegistrarSignatureDigest, | ||
| } from 'apps/web/src/utils/usernames'; | ||
| import { Dispatch, SetStateAction, useCallback, useMemo, useState } from 'react'; | ||
| import { Dispatch, SetStateAction, useCallback, useMemo, useRef, useState } from 'react'; | ||
| import { encodeFunctionData, namehash } from 'viem'; | ||
| import { useAccount } from 'wagmi'; | ||
| import { secondsInYears } from 'apps/web/src/utils/secondsInYears'; | ||
| import L2ReverseRegistrarAbi from 'apps/web/src/abis/L2ReverseRegistrarAbi'; | ||
| import { getFunctionSelector, type AbiFunction, type WriteContractParameters } from 'viem'; | ||
| import { useSignMessage } from 'wagmi'; | ||
|
|
||
| type UseRegisterNameCallbackReturnType = { | ||
| callback: () => Promise<void>; | ||
|
|
@@ -35,6 +38,11 @@ type UseRegisterNameCallbackReturnType = { | |
| hasExistingBasename: boolean; | ||
| batchCallsStatus: BatchCallsStatus; | ||
| registerNameStatus: WriteTransactionWithReceiptStatus; | ||
| signMessageForReverseRecord: () => Promise<{ | ||
| coinTypes: readonly bigint[]; | ||
| signatureExpiry: bigint; | ||
| signature: `0x${string}`; | ||
| }>; | ||
| }; | ||
|
|
||
| export function useRegisterNameCallback( | ||
|
|
@@ -50,6 +58,7 @@ export function useRegisterNameCallback( | |
| const { paymasterService: paymasterServiceEnabled } = useCapabilitiesSafe({ | ||
| chainId: basenameChain.id, | ||
| }); | ||
| const { signMessageAsync } = useSignMessage(); | ||
|
|
||
| // If user has a basename, reverse record is set to false | ||
| const { data: baseEnsName, isLoading: baseEnsNameIsLoading } = useBaseEnsName({ | ||
|
|
@@ -62,6 +71,12 @@ export function useRegisterNameCallback( | |
| ); | ||
|
|
||
| const [reverseRecord, setReverseRecord] = useState<boolean>(!hasExistingBasename); | ||
| const [signatureError, setSignatureError] = useState<Error | null>(null); | ||
| const reverseSigPayloadRef = useRef<{ | ||
| coinTypes: readonly bigint[]; | ||
| signatureExpiry: bigint; | ||
| signature: `0x${string}`; | ||
| } | null>(null); | ||
|
|
||
| // Transaction with paymaster enabled | ||
| const { initiateBatchCalls, batchCallsStatus, batchCallsIsLoading, batchCallsError } = | ||
|
|
@@ -85,9 +100,33 @@ export function useRegisterNameCallback( | |
| const normalizedName = normalizeEnsDomainName(name); | ||
| const isDiscounted = Boolean(discountKey && validationData); | ||
|
|
||
| const signMessageForReverseRecord = useCallback(async () => { | ||
| if (!address) throw new Error('No address'); | ||
| const reverseRegistrar = USERNAME_L2_REVERSE_REGISTRAR_ADDRESSES[basenameChain.id]; | ||
|
|
||
| const functionAbi = L2ReverseRegistrarAbi.find( | ||
| (f) => f.type === 'function' && f.name === 'setNameForAddrWithSignature', | ||
| ) as unknown as AbiFunction; | ||
| const signatureExpiry = BigInt(Math.floor(Date.now() / 1000) + 5 * 60); | ||
| const { digest, coinTypes } = buildReverseRegistrarSignatureDigest({ | ||
| reverseRegistrar, | ||
| functionAbi, | ||
| address, | ||
| chainId: basenameChain.id, | ||
| name, | ||
| signatureExpiry, | ||
| }); | ||
| const signature = await signMessageAsync({ message: { raw: digest } }); | ||
|
|
||
| const payload = { coinTypes, signatureExpiry, signature } as const; | ||
| reverseSigPayloadRef.current = payload; | ||
| return payload; | ||
| }, [address, basenameChain.id, name, signMessageAsync]); | ||
|
|
||
| // Callback | ||
| const registerName = useCallback(async () => { | ||
| if (!address) return; | ||
| setSignatureError(null); | ||
|
|
||
| const addressData = encodeFunctionData({ | ||
| abi: L2ResolverAbi, | ||
|
|
@@ -114,16 +153,43 @@ export function useRegisterNameCallback( | |
| ], | ||
| }); | ||
|
|
||
| let coinTypesForRequest: readonly bigint[] = []; | ||
| let signatureExpiryForRequest: bigint = 0n; | ||
| let signatureForRequest: `0x${string}` = '0x'; | ||
|
|
||
| if (!paymasterServiceEnabled && reverseRecord) { | ||
| let payload = reverseSigPayloadRef.current; | ||
| if (!payload) { | ||
| try { | ||
| payload = await signMessageForReverseRecord(); | ||
| } catch (e) { | ||
| logError(e, 'Reverse record signature step failed'); | ||
| const msg = e instanceof Error && e.message ? e.message : 'Unknown error'; | ||
| setSignatureError(new Error(`Could not prepare reverse record signature: ${msg}`)); | ||
| return; | ||
| } | ||
| } | ||
| if (!payload) { | ||
| setSignatureError(new Error('Could not prepare reverse record signature')); | ||
| return; | ||
| } | ||
| coinTypesForRequest = payload?.coinTypes ?? []; | ||
| signatureExpiryForRequest = payload?.signatureExpiry ?? '0x'; | ||
| signatureForRequest = payload?.signature ?? '0x'; | ||
| } | ||
|
|
||
| const reverseRecordForRequest = paymasterServiceEnabled ? false : reverseRecord; | ||
|
|
||
| const registerRequest = { | ||
| name: normalizedName, // The name being registered. | ||
| owner: address, // The address of the owner for the name. | ||
| duration: secondsInYears(years), // The duration of the registration in seconds. | ||
| resolver: UPGRADEABLE_L2_RESOLVER_ADDRESSES[basenameChain.id], // The address of the resolver to set for this name. | ||
| data: [addressData, baseCointypeData, nameData], // Multicallable data bytes for setting records in the associated resolver upon registration. | ||
| reverseRecord, // Bool to decide whether to set this name as the "primary" name for the `owner`. | ||
| coinTypes: [], | ||
| signatureExpiry: 0, | ||
| signature: '0x', | ||
| reverseRecord: reverseRecordForRequest, // When using paymaster (atomic batch), set via separate call instead of signature flow. | ||
| coinTypes: coinTypesForRequest, | ||
| signatureExpiry: signatureExpiryForRequest, | ||
| signature: signatureForRequest, | ||
| }; | ||
|
|
||
| try { | ||
|
|
@@ -134,7 +200,9 @@ export function useRegisterNameCallback( | |
| functionName: isDiscounted ? 'discountedRegister' : 'register', | ||
| args: isDiscounted ? [registerRequest, discountKey, validationData] : [registerRequest], | ||
| value, | ||
| }); | ||
| chain: basenameChain, | ||
| account: address, | ||
| } as unknown as WriteContractParameters); | ||
|
||
| } else { | ||
| await initiateBatchCalls({ | ||
| contracts: [ | ||
|
|
@@ -185,11 +253,12 @@ export function useRegisterNameCallback( | |
| return { | ||
| callback: registerName, | ||
| isPending: registerNameIsLoading || batchCallsIsLoading, | ||
|
||
| error: registerNameError ?? batchCallsError, | ||
| error: signatureError ?? registerNameError ?? batchCallsError, | ||
| reverseRecord, | ||
| setReverseRecord, | ||
| hasExistingBasename, | ||
| batchCallsStatus, | ||
| registerNameStatus, | ||
| signMessageForReverseRecord, | ||
amiecorso marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| }; | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.