-
Notifications
You must be signed in to change notification settings - Fork 192
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Get the EVM address for a given Farcaster User (#114)
Co-authored-by: Sneh Koul <[email protected]>
- Loading branch information
1 parent
2c101e8
commit 926bc30
Showing
15 changed files
with
310 additions
and
36 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
--- | ||
'@coinbase/onchainkit': patch | ||
--- | ||
|
||
- Added `getFarcasterUserAddress` utility to extract user's custody and/or verified addresses. | ||
- Updates the version of `@types/jest` package |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
import { getFarcasterUserAddress } from './getFarcasterUserAddress'; | ||
import { getCustodyAddressForFidNeynar } from '../utils/neynar/user/getCustodyAddressForFidNeynar'; | ||
import { getVerifiedAddressesForFidNeynar } from '../utils/neynar/user/getVerifiedAddressesForFidNeynar'; | ||
|
||
jest.mock('../utils/neynar/user/getCustodyAddressForFidNeynar'); | ||
jest.mock('../utils/neynar/user/getVerifiedAddressesForFidNeynar'); | ||
|
||
describe('getFarcasterUserAddress function', () => { | ||
beforeEach(() => { | ||
// Reset mocks before each test | ||
jest.clearAllMocks(); | ||
}); | ||
|
||
it('should return null if any API call fails', async () => { | ||
const error = new Error('Something went wrong'); | ||
(getVerifiedAddressesForFidNeynar as jest.Mock).mockRejectedValue(error); | ||
const result = await getFarcasterUserAddress(123); | ||
expect(result).toBeNull(); | ||
}); | ||
|
||
it('should return both custody and verified addresses by default', async () => { | ||
const expectedCustodyAddress = 'mock-custody-address'; | ||
(getCustodyAddressForFidNeynar as jest.Mock).mockResolvedValue(expectedCustodyAddress); | ||
(getVerifiedAddressesForFidNeynar as jest.Mock).mockResolvedValue([expectedCustodyAddress]); | ||
|
||
const result = await getFarcasterUserAddress(123); | ||
expect(result).toEqual({ | ||
custodyAddress: expectedCustodyAddress, | ||
verifiedAddresses: [expectedCustodyAddress], | ||
}); | ||
}); | ||
|
||
it('should return null if both hasCustodyAddresses and hasVerifiedAddresses are false', async () => { | ||
const result = await getFarcasterUserAddress(123, { | ||
hasCustodyAddresses: false, | ||
hasVerifiedAddresses: false, | ||
}); | ||
expect(result).toEqual({}); | ||
}); | ||
|
||
it('should return both custodyAddress and verifiedAddresses when both options are true', async () => { | ||
const expectedCustodyAddress = 'mock-custody-address'; | ||
const expectedVerifiedAddresses = ['mock-verified-address-1', 'mock-verified-address-2']; | ||
(getCustodyAddressForFidNeynar as jest.Mock).mockResolvedValue(expectedCustodyAddress); | ||
(getVerifiedAddressesForFidNeynar as jest.Mock).mockResolvedValue(expectedVerifiedAddresses); | ||
const result = await getFarcasterUserAddress(123, { | ||
hasCustodyAddresses: true, | ||
hasVerifiedAddresses: true, | ||
}); | ||
expect(result).toEqual({ | ||
custodyAddress: expectedCustodyAddress, | ||
verifiedAddresses: expectedVerifiedAddresses, | ||
}); | ||
}); | ||
|
||
it('should only return custodyAddress when hasVerifiedAddresses is false', async () => { | ||
const expectedCustodyAddress = 'mock-custody-address'; | ||
(getCustodyAddressForFidNeynar as jest.Mock).mockResolvedValue(expectedCustodyAddress); | ||
const result = await getFarcasterUserAddress(123, { | ||
hasVerifiedAddresses: false, | ||
}); | ||
expect(result).toEqual({ custodyAddress: expectedCustodyAddress }); | ||
}); | ||
|
||
it('should only return verifiedAddresses when hasCustodyAddresses is false', async () => { | ||
const expectedVerifiedAddresses = ['mock-verified-address-1', 'mock-verified-address-2']; | ||
(getVerifiedAddressesForFidNeynar as jest.Mock).mockResolvedValue(expectedVerifiedAddresses); | ||
const result = await getFarcasterUserAddress(123, { | ||
hasCustodyAddresses: false, | ||
}); | ||
expect(result).toEqual({ verifiedAddresses: expectedVerifiedAddresses }); | ||
}); | ||
|
||
it('should call getCustodyAddressForFidNeynar and getVerifiedAddressesForFidNeynar with the default neynarApiKey if not provided', async () => { | ||
await getFarcasterUserAddress(123); | ||
expect(getCustodyAddressForFidNeynar).toHaveBeenCalledWith(123, undefined); | ||
expect(getVerifiedAddressesForFidNeynar).toHaveBeenCalledWith(123, undefined); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
import { getCustodyAddressForFidNeynar } from '../utils/neynar/user/getCustodyAddressForFidNeynar'; | ||
import { getVerifiedAddressesForFidNeynar } from '../utils/neynar/user/getVerifiedAddressesForFidNeynar'; | ||
import { GetFarcasterUserAddressResponse } from './types'; | ||
|
||
type GetFarcasterUserAddressOptions = | ||
| { | ||
neynarApiKey?: string; // default to onchain-kit's default key | ||
hasCustodyAddresses?: boolean; // default to true | ||
hasVerifiedAddresses?: boolean; // default to true | ||
} | ||
| undefined; | ||
|
||
/** | ||
* Get the user address for a given fid | ||
* @param fid The farcaster id | ||
* @param GetFarcasterUserAddressOptions The options to specify the type of addresses to get and the neynar api key | ||
* @returns the custory address and/or verified addresses. If there is an error, it returns null | ||
*/ | ||
async function getFarcasterUserAddress( | ||
fid: number, | ||
options?: GetFarcasterUserAddressOptions, | ||
): Promise<GetFarcasterUserAddressResponse | null> { | ||
try { | ||
const hasCustodyAddresses = options?.hasCustodyAddresses ?? true; | ||
const hasVerifiedAddresses = options?.hasVerifiedAddresses ?? true; | ||
const response: GetFarcasterUserAddressResponse = {}; | ||
|
||
if (hasCustodyAddresses) { | ||
const custodyAddress = await getCustodyAddressForFidNeynar(fid, options?.neynarApiKey); | ||
if (custodyAddress) { | ||
response.custodyAddress = custodyAddress; | ||
} | ||
} | ||
|
||
if (hasVerifiedAddresses) { | ||
const verifiedAddresses = await getVerifiedAddressesForFidNeynar(fid, options?.neynarApiKey); | ||
if (verifiedAddresses) { | ||
response.verifiedAddresses = verifiedAddresses; | ||
} | ||
} | ||
|
||
return response; | ||
} catch (e) { | ||
return null; | ||
} | ||
} | ||
|
||
export { getFarcasterUserAddress }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export { getFarcasterUserAddress } from './getFarcasterUserAddress'; | ||
export type { GetFarcasterUserAddressResponse } from './types'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
/** | ||
* GetFarcasterUserAddressResponse | ||
* | ||
* Note: exported as public Type | ||
*/ | ||
export type GetFarcasterUserAddressResponse = { | ||
custodyAddress?: string; | ||
verifiedAddresses?: string[]; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import { version } from '../../version'; | ||
import { FetchError } from './exceptions/FetchError'; | ||
import { NEYNAR_DEFAULT_API_KEY } from './frame/neynarFrameFunctions'; | ||
|
||
export async function getDataFromNeynar(url: string, apiKey: string = NEYNAR_DEFAULT_API_KEY) { | ||
const options = { | ||
method: 'GET', | ||
url: url, | ||
headers: { | ||
accept: 'application/json', | ||
api_key: apiKey, | ||
'content-type': 'application/json', | ||
onchainkit_version: version, | ||
}, | ||
}; | ||
const resp = await fetch(options.url, options); | ||
if (resp.status !== 200) { | ||
throw new FetchError(`non-200 status returned from neynar : ${resp.status}`); | ||
} | ||
return await resp.json(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import { version } from '../../version'; | ||
import { FetchError } from './exceptions/FetchError'; | ||
import { NEYNAR_DEFAULT_API_KEY } from './frame/neynarFrameFunctions'; | ||
|
||
export async function postDataToNeynar( | ||
url: string, | ||
apiKey: string = NEYNAR_DEFAULT_API_KEY, | ||
data: any, | ||
) { | ||
const options = { | ||
method: 'POST', | ||
url: url, | ||
headers: { | ||
accept: 'application/json', | ||
api_key: apiKey, | ||
'content-type': 'application/json', | ||
onchainkit_version: version, | ||
}, | ||
body: JSON.stringify(data), | ||
}; | ||
const resp = await fetch(options.url, options); | ||
if (resp.status !== 200) { | ||
throw new FetchError(`non-200 status returned from neynar : ${resp.status}`); | ||
} | ||
return await resp.json(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import { version } from '../../../version'; | ||
import { FetchError } from '../exceptions/FetchError'; | ||
import { getDataFromNeynar } from '../getDataFormNeynar'; | ||
import { NEYNAR_DEFAULT_API_KEY } from '../frame/neynarFrameFunctions'; | ||
|
||
export async function getCustodyAddressForFidNeynar( | ||
fid: number, | ||
apiKey: string = NEYNAR_DEFAULT_API_KEY, | ||
): Promise<string> { | ||
const url = `https://api.neynar.com/v1/farcaster/custody-address?fid=${fid}`; | ||
|
||
const responseBody = await getDataFromNeynar(url, apiKey); | ||
|
||
if (!responseBody || !responseBody.result || !responseBody.result.custodyAddress) { | ||
throw new Error('No custody address found for FID ' + fid); | ||
} | ||
|
||
return responseBody.result.custodyAddress; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import { NEYNAR_DEFAULT_API_KEY } from '../frame/neynarFrameFunctions'; | ||
import { getDataFromNeynar } from '../getDataFormNeynar'; | ||
|
||
export async function getVerifiedAddressesForFidNeynar( | ||
fid: number, | ||
apiKey: string = NEYNAR_DEFAULT_API_KEY, | ||
): Promise<string[]> { | ||
const url = `https://api.neynar.com/v1/farcaster/verifications?fid=${fid}`; | ||
|
||
const responseBody = await getDataFromNeynar(url, apiKey); | ||
|
||
if ( | ||
!responseBody || | ||
!responseBody.result || | ||
!responseBody.result.verifications || | ||
responseBody.result.verifications.length === 0 | ||
) { | ||
throw new Error('No verified addresses found for FID ' + fid); | ||
} | ||
|
||
return responseBody.result.verifications; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.