Skip to content

Commit

Permalink
NFT checker 2.0 (#6293)
Browse files Browse the repository at this point in the history
  • Loading branch information
brunobar79 authored Dec 13, 2024
1 parent e09a39a commit 341b72c
Show file tree
Hide file tree
Showing 40 changed files with 327 additions and 199 deletions.
13 changes: 13 additions & 0 deletions android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,19 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity-alias>
<activity-alias
android:name="me.rainbow.MainActivityRedacted"
android:enabled="false"
android:icon="@mipmap/redacted"
android:roundIcon="@mipmap/redacted_round"
android:targetActivity=".MainActivity"
android:exported="true"
>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity-alias>

<!-- Branch init -->
<meta-data android:name="io.branch.sdk.BranchKey" android:value="${BRANCH_KEY}" />
Expand Down
5 changes: 5 additions & 0 deletions android/app/src/main/res/mipmap-anydpi-v26/redacted.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@mipmap/redacted_background"/>
<foreground android:drawable="@mipmap/redacted_foreground"/>
</adaptive-icon>
5 changes: 5 additions & 0 deletions android/app/src/main/res/mipmap-anydpi-v26/redacted_round.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@mipmap/redacted_background"/>
<foreground android:drawable="@mipmap/redacted_foreground"/>
</adaptive-icon>
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added ios/AppIcons/[email protected]
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added ios/AppIcons/[email protected]
2 changes: 1 addition & 1 deletion ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2597,7 +2597,7 @@ SPEC CHECKSUMS:
ToolTipMenu: 8ac61aded0fbc4acfe7e84a7d0c9479d15a9a382
TurboHaptics: 6381613d33ab97aeb30d9b15c3df94dc616a25e4
VisionCamera: 2af28201c3de77245f8c58b7a5274d5979df70df
Yoga: 88480008ccacea6301ff7bf58726e27a72931c8d
Yoga: 04f1db30bb810187397fa4c37dd1868a27af229c

PODFILE CHECKSUM: 98c3fc206d7041ac7388693bb0753109d1884b57

Expand Down
354 changes: 181 additions & 173 deletions ios/Rainbow.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

9 changes: 9 additions & 0 deletions ios/Rainbow/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,15 @@
<key>UIPrerenderedIcon</key>
<false/>
</dict>
<key>redacted</key>
<dict>
<key>CFBundleIconFiles</key>
<array>
<string>redacted</string>
</array>
<key>UIPrerenderedIcon</key>
<false/>
</dict>
</dict>
<key>CFBundlePrimaryIcon</key>
<dict>
Expand Down
25 changes: 22 additions & 3 deletions src/appIcons/appIcons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ import AppIconZorb from '@/assets/appIconZorb.png';
import AppIconPoolboy from '@/assets/appIconPoolboy.png';
import AppIconAdworld from '@/assets/appIconAdworld.png';
import AppIconFarcaster from '@/assets/appIconFarcaster.png';
import { TokenGateCheckerNetwork } from '@/featuresToUnlock/tokenGatedUtils';
import AppIconRedacted from '@/assets/appIconRedacted.png';
import { TokenGateCheckerNetwork, TokenInfo } from '@/featuresToUnlock/tokenGatedUtils';
import { Network } from '@/state/backendNetworks/types';

// optimism app icon unlocking NFTs
Expand Down Expand Up @@ -57,14 +58,17 @@ const ADWORLD_NFT_ADDRESS: EthereumAddress = '0x6171f829e107f70b58d67594c6b62a7d
// farcaster app icon unlocking NFTs
const FARCASTER_NFT_ADDRESS: EthereumAddress = '0x76843c8f8a369d29c719141a065ff561abe2420b';

// redacted app icon unlocking NFTs
const REDACTED_NFT_ADDRESS: EthereumAddress = '0x8C3f001A893f3B561dBCaa80f075ecc7A5B92690';

export interface AppIcon {
accentColor: string;
displayName: string;
image: ImageSourcePropType;
}

export interface UnlockableAppIcon extends AppIcon {
unlockingNFTs: Partial<Record<TokenGateCheckerNetwork, EthereumAddress[]>>;
unlockingNFTs: Partial<Record<TokenGateCheckerNetwork, EthereumAddress[] | TokenInfo[]>>;
}

export type FreeAppIconKey = 'og' | 'pixel';
Expand All @@ -80,7 +84,8 @@ export type UnlockableAppIconKey =
| 'zorb'
| 'poolboy'
| 'adworld'
| 'farcaster';
| 'farcaster'
| 'redacted';

export type AppIconKey = FreeAppIconKey | UnlockableAppIconKey;

Expand Down Expand Up @@ -173,4 +178,18 @@ export const unlockableAppIcons: Record<UnlockableAppIconKey, UnlockableAppIcon>
image: AppIconFarcaster,
unlockingNFTs: { [Network.base]: [FARCASTER_NFT_ADDRESS] },
},
redacted: {
accentColor: '#001E59',
displayName: 'Redacted Rainbow',
image: AppIconRedacted,
unlockingNFTs: { [Network.base]: [REDACTED_NFT_ADDRESS] },
},

// This is an example of how to add a new test app icon with an ERC-1155 NFT
// newTest_1155: {
// accentColor: '#FF0000',
// displayName: 'NewTest1155',
// image: AppIconTest1155,
// unlockingNFTs: { [Network.base]: [{ account: '0xae2e09aAB7a190664E36daB48c576230E19a0B44', id: 2 }] },
// }
};
Binary file added src/assets/appIconRedacted.png
Binary file added src/assets/[email protected]
Binary file added src/assets/[email protected]
63 changes: 49 additions & 14 deletions src/featuresToUnlock/tokenGatedUtils.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { Contract } from '@ethersproject/contracts';
import { EthereumAddress } from '@/entities';
import { getProvider } from '@/handlers/web3';
import { tokenGateCheckerAbi } from '@/references';
import { Network } from '@/state/backendNetworks/types';
import { useBackendNetworksStore } from '@/state/backendNetworks/backendNetworks';
import { Interface } from '@ethersproject/abi';

export type TokenGateCheckerNetwork =
| Network.arbitrum
Expand All @@ -12,29 +12,64 @@ export type TokenGateCheckerNetwork =
| Network.mainnet
| Network.polygon
| Network.bsc
| Network.base;

| Network.base
| Network.gnosis
| Network.avalanche;
const TOKEN_GATE_CHECKER_ADDRESS: Record<TokenGateCheckerNetwork, EthereumAddress> = {
[Network.arbitrum]: '0x2a0332e28913a06fa924d40a3e2160f763010417',
[Network.mainnet]: '0x47c9c137fc9aa5ccdbea707b0b27d52780565476',
[Network.optimism]: '0x400a9f1bb1db80643c33710c2232a0d74ef5cff1',
[Network.polygon]: '0x400a9f1bb1db80643c33710c2232a0d74ef5cff1',
[Network.zora]: '0x12a39421c23f4d3f788c33f0f9281652ac4f909a',
[Network.bsc]: '0x5a9d9ffbd5a22f2790af726550920b845c3a6b35',
[Network.base]: '0x7edddf0b8e7471e0ebf0df67ad179598c0bef695',
[Network.arbitrum]: '0x5A9D9FFBd5a22f2790AF726550920B845c3A6B35',
[Network.mainnet]: '0xc4A8619B3980d84F6d59d416d415007A1217fEc8',
[Network.optimism]: '0x5A9D9FFBd5a22f2790AF726550920B845c3A6B35',
[Network.polygon]: '0x3e3937C119BD854059844D3D03A8116a18Afa409',
[Network.zora]: '0x75efed6B8AF6B0490c2899e489c58EF26E3D0898',
[Network.bsc]: '0x50a42aB85A09e24229e42dAd31Cb44B42E83b2De',
[Network.base]: '0xa5d7b264ad7039F54A670c27Fe5A64CAd0FE0cCe',
[Network.gnosis]: '0x2488F7B6FD1A0949391fE6a533D7E5c4704173E2',
[Network.avalanche]: '0x2a0332E28913A06Fa924d40A3E2160f763010417',
};

/***
* Check if wallets own NFTs of type ERC-721
* We only need the NFT address to check for ownership
*/

export const checkIfWalletsOwnNft = async (
tokenAddress: EthereumAddress[],
tokenAddresses: EthereumAddress[],
network: TokenGateCheckerNetwork,
walletsToCheck: EthereumAddress[]
) => {
const p = getProvider({ chainId: useBackendNetworksStore.getState().getChainsIdByName()[network] });
try {
const p = getProvider({ chainId: useBackendNetworksStore.getState().getChainsIdByName()[network] });
const iface = new Interface(tokenGateCheckerAbi);

const data = iface.encodeFunctionData('areOwners(address[], address[])', [tokenAddresses, walletsToCheck]);
const found = await p.call({ to: TOKEN_GATE_CHECKER_ADDRESS[network], data });
return found;
} catch (e) {
return false;
}
};

const contractInstance = new Contract(TOKEN_GATE_CHECKER_ADDRESS[network], tokenGateCheckerAbi, p);
export interface TokenInfo {
account: EthereumAddress;
id: number;
}

/***
* Check if wallets own NFTs of type ERC-1155
* In this case we need to also check the id of the NFTs
*/

export const checkIfWalletsOwnNft1155 = async (
tokenInfo: TokenInfo[],
network: TokenGateCheckerNetwork,
walletsToCheck: EthereumAddress[]
) => {
try {
const found = contractInstance.areOwners(tokenAddress, walletsToCheck);
const p = getProvider({ chainId: useBackendNetworksStore.getState().getChainsIdByName()[network] });
const iface = new Interface(tokenGateCheckerAbi);
const data = iface.encodeFunctionData('areOwners(TokenInfo[], address[])', [tokenInfo, walletsToCheck]);
const found = await p.call({ to: TOKEN_GATE_CHECKER_ADDRESS[network], data });

return found;
} catch (e) {
return false;
Expand Down
26 changes: 22 additions & 4 deletions src/featuresToUnlock/unlockableAppIconCheck.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { TokenGateCheckerNetwork, checkIfWalletsOwnNft } from './tokenGatedUtils';
import { TokenGateCheckerNetwork, TokenInfo, checkIfWalletsOwnNft, checkIfWalletsOwnNft1155 } from './tokenGatedUtils';
import { EthereumAddress } from '@/entities';
import { Navigation } from '@/navigation';
import { RainbowError, logger } from '@/logger';
Expand All @@ -22,18 +22,36 @@ export const unlockableAppIconCheck = async (appIconKey: UnlockableAppIconKey, w

const handled = unlockableAppIconStorage.getBoolean(appIconKey);

logger.debug(`[unlockableAppIconCheck]: ${appIconKey} was handled? ${handled}`);

if (handled) return false;

logger.debug(`[unlockableAppIconCheck]: ${appIconKey} was handled? ${handled}`);

try {
const found = (
await Promise.all(
(Object.keys(appIcon.unlockingNFTs) as TokenGateCheckerNetwork[]).map(async network => {
const nfts = appIcon.unlockingNFTs[network];
if (!nfts) return;
logger.debug(`[unlockableAppIconCheck]: Checking ${appIconKey} on network ${network}`);
return await checkIfWalletsOwnNft(nfts, network, walletsToCheck);
const non1155s: EthereumAddress[] = [];
const all1155s: TokenInfo[] = [];

const values = Object.values(nfts);
values.forEach(value => {
if (typeof value === 'string') {
non1155s.push(value);
} else {
all1155s.push(value);
}
});
const allChecks = [];
if (non1155s.length > 0) {
allChecks.push(checkIfWalletsOwnNft(non1155s, network, walletsToCheck));
}
if (all1155s.length > 0) {
allChecks.push(checkIfWalletsOwnNft1155(all1155s, network, walletsToCheck));
}
return Promise.all(allChecks);
})
)
).some(result => !!result);
Expand Down
2 changes: 2 additions & 0 deletions src/languages/en_US.json
Original file line number Diff line number Diff line change
Expand Up @@ -866,6 +866,8 @@
"adworld_title": "Welcome to Rainbow World!",
"farcaster_description": "Our most boring app icon ever, inspired by Warpcast's bland minimalism.",
"farcaster_title": "You've unlocked\nRainbowcast",
"redacted_description": "A stupi-exclusive Rainbow icon for the true Gdupi enjoyers.",
"redacted_title": "You've unlocked\n Redacted Rainbow",
"button": "Snag the icon"
},
"explain": {
Expand Down
22 changes: 18 additions & 4 deletions src/references/token-gate-checker-abi.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,25 @@
{
"inputs": [
{ "internalType": "address[]", "name": "targets", "type": "address[]" },
{ "internalType": "address[]", "name": "potentialOwners", "type": "address[]" }
],
"name": "areOwners",
"outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address[]",
"name": "potentialOwners",
"type": "address[]"
}
"components": [
{ "internalType": "address", "name": "account", "type": "address" },
{ "internalType": "uint256", "name": "id", "type": "uint256" }
],
"internalType": "struct NftChecker.TokenInfo[]",
"name": "targets",
"type": "tuple[]"
},
{ "internalType": "address[]", "name": "potentialOwners", "type": "address[]" }
],
"name": "areOwners",
"outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
Expand Down

0 comments on commit 341b72c

Please sign in to comment.