Skip to content

Commit 04fc181

Browse files
authored
Merge branch 'main' into campaign-sats
2 parents c7ada01 + 9ba4fa2 commit 04fc181

File tree

8 files changed

+137
-10
lines changed

8 files changed

+137
-10
lines changed

background/lib/nfts.ts

+19
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ import {
1313
TransferredNFT,
1414
} from "../nfts"
1515
import { UNIXTime } from "../types"
16+
import { MEZO_TESTNET } from "../constants"
17+
import { NFT_COLLECTION_ID } from "../services/campaign/matsnet-nft"
1618

1719
function groupChainsByAddress(accounts: AddressOnNetwork[]) {
1820
return accounts.reduce<{ [address: string]: string[] }>((acc, account) => {
@@ -100,6 +102,23 @@ export function getNFTCollections(
100102
collections.push(await getPoapCollections(address))
101103
}
102104

105+
const campaignChains = chainIDs.filter((chainID) =>
106+
NFT_PROVIDER_TO_CHAIN.campaign.includes(chainID),
107+
)
108+
109+
if (campaignChains.length) {
110+
collections.push({
111+
id: NFT_COLLECTION_ID,
112+
name: "TahoXMezo",
113+
nftCount: undefined,
114+
owner: address,
115+
hasBadges: false,
116+
network: MEZO_TESTNET,
117+
floorPrice: undefined,
118+
thumbnailURL: undefined,
119+
})
120+
}
121+
103122
const simpleHashChains = chainIDs.filter((chainID) =>
104123
NFT_PROVIDER_TO_CHAIN.simplehash.includes(chainID),
105124
)

background/lib/posthog.ts

+4
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ export enum AnalyticsEvent {
1616
DAPP_CONNECTED = "Dapp Connected",
1717
VAULT_MIGRATION = "Migrate to newer vault version",
1818
VAULT_MIGRATION_FAILED = "Vault version migration failed",
19+
// Campaign events
20+
CAMPAIGN_MEZO_NFT_ELIGIBLE_BANNER = "in_wallet-claim_sats",
21+
CAMPAIGN_MEZO_NFT_BORROW_BANNER = "in_wallet-borrow_musd",
22+
CAMPAIGN_MEZO_NFT_CLAIM_NFT_BANNER = "in_wallet-visit_store",
1923
}
2024

2125
export enum OneTimeAnalyticsEvent {

background/nfts.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,13 @@ import {
66
AVALANCHE,
77
BINANCE_SMART_CHAIN,
88
ZK_SYNC,
9+
MEZO_TESTNET,
910
} from "./constants"
1011
import { EVMNetwork } from "./networks"
1112
// Networks that are not added to this struct will
1213
// not have an in-wallet NFT tab
1314
export const CHAIN_ID_TO_NFT_METADATA_PROVIDER: {
14-
[chainID: string]: ("simplehash" | "poap")[]
15+
[chainID: string]: ("simplehash" | "poap" | "campaign")[]
1516
} = {
1617
[ETHEREUM.chainID]: ["simplehash", "poap"],
1718
[POLYGON.chainID]: ["simplehash"],
@@ -20,10 +21,12 @@ export const CHAIN_ID_TO_NFT_METADATA_PROVIDER: {
2021
[AVALANCHE.chainID]: ["simplehash"],
2122
[BINANCE_SMART_CHAIN.chainID]: ["simplehash"],
2223
[ZK_SYNC.chainID]: ["simplehash"],
24+
[MEZO_TESTNET.chainID]: ["campaign"],
2325
}
2426

2527
export const NFT_PROVIDER_TO_CHAIN = {
2628
poap: [ETHEREUM.chainID],
29+
campaign: [MEZO_TESTNET.chainID],
2730
simplehash: [
2831
ETHEREUM.chainID,
2932
POLYGON.chainID,

background/redux-slices/utils/nfts-utils.ts

+2
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,8 @@ export const getFilteredCollections = (
208208
return collections
209209
.filter(
210210
(collection) =>
211+
collection.nftCount &&
212+
collection.nftCount > 0 &&
211213
isEnabledFilter(collection.id, filters.collections) &&
212214
isEnabledFilter(collection.owner, filters.accounts),
213215
)

background/services/campaign/index.ts

+19-3
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import MEZO_CAMPAIGN, { MezoClaimStatus } from "./matsnet-nft"
1616
import { isConfirmedEVMTransaction } from "../../networks"
1717
import { Campaigns } from "./types"
1818
import logger from "../../lib/logger"
19+
import { AnalyticsEvent } from "../../lib/posthog"
1920
import { SECOND } from "../../constants"
2021

2122
dayjs.extend(isBetween)
@@ -254,7 +255,12 @@ export default class CampaignService extends BaseService<Events> {
254255
),
255256
},
256257
callback: () => {
257-
browser.tabs.create({ url: "https://mezo.org/matsnet" })
258+
this.analyticsService.sendAnalyticsEvent(
259+
AnalyticsEvent.CAMPAIGN_MEZO_NFT_ELIGIBLE_BANNER,
260+
)
261+
browser.tabs.create({
262+
url: "https://mezo.org/matsnet/borrow?src=taho-claim-sats-banner",
263+
})
258264
this.preferenceService.markDismissableItemAsShown(
259265
MEZO_CAMPAIGN.notificationIds.eligible,
260266
)
@@ -277,7 +283,12 @@ export default class CampaignService extends BaseService<Events> {
277283
),
278284
},
279285
callback: () => {
280-
browser.tabs.create({ url: "https://mezo.org/matsnet/borrow" })
286+
this.analyticsService.sendAnalyticsEvent(
287+
AnalyticsEvent.CAMPAIGN_MEZO_NFT_BORROW_BANNER,
288+
)
289+
browser.tabs.create({
290+
url: "https://mezo.org/matsnet/borrow?src=taho-borrow-banner",
291+
})
281292
this.preferenceService.markDismissableItemAsShown(
282293
MEZO_CAMPAIGN.notificationIds.canBorrow,
283294
)
@@ -301,7 +312,12 @@ export default class CampaignService extends BaseService<Events> {
301312
),
302313
},
303314
callback: () => {
304-
browser.tabs.create({ url: "https://mezo.org/matsnet/borrow" })
315+
this.analyticsService.sendAnalyticsEvent(
316+
AnalyticsEvent.CAMPAIGN_MEZO_NFT_CLAIM_NFT_BANNER,
317+
)
318+
browser.tabs.create({
319+
url: "https://mezo.org/matsnet/store?src=taho-claim-nft-banner",
320+
})
305321
this.preferenceService.markDismissableItemAsShown(
306322
MEZO_CAMPAIGN.notificationIds.canClaimNFT,
307323
)

background/services/campaign/matsnet-nft.ts

+4
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ export type MezoCampaignState = {
1111
state: MezoClaimStatus
1212
}
1313

14+
export const NFT_CONTRACT_ADDRESS = "0x2A22371b53E6070AF6e38dfFC4228496b469D7FA"
15+
1416
export const CAMPAIGN_ID = "mezo-nft-claim"
1517

1618
export type MezoCampaign = {
@@ -22,6 +24,8 @@ export type MezoCampaign = {
2224
const prefixWithCampaignId = (str: string): `campaign::${string}` =>
2325
`campaign::${CAMPAIGN_ID}-${str}`
2426

27+
export const NFT_COLLECTION_ID = prefixWithCampaignId("nft-collection")
28+
2529
export const IS_ELIGIBLE_NOTIFICATION_ID = prefixWithCampaignId(
2630
"eligible-notification",
2731
)

background/services/nfts/index.ts

+68-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { fetchJson } from "@ethersproject/web"
2+
import { Contract } from "ethers"
13
import { AddressOnNetwork } from "../../accounts"
24
import { getNFTCollections, getNFTs, getNFTsTransfers } from "../../lib/nfts"
35
import { getSimpleHashNFTs } from "../../lib/simple-hash"
@@ -9,7 +11,13 @@ import ChainService from "../chain"
911
import { ServiceCreatorFunction, ServiceLifecycleEvents } from "../types"
1012
import { getOrCreateDB, NFTsDatabase, FreshCollectionsMap } from "./db"
1113
import { getUNIXTimestamp, normalizeEVMAddress } from "../../lib/utils"
12-
import { MINUTE } from "../../constants"
14+
import { MEZO_TESTNET, MINUTE } from "../../constants"
15+
import { sameNetwork } from "../../networks"
16+
import {
17+
NFT_COLLECTION_ID,
18+
NFT_CONTRACT_ADDRESS,
19+
} from "../campaign/matsnet-nft"
20+
import { isDisabled } from "../../features"
1321

1422
interface Events extends ServiceLifecycleEvents {
1523
isReloadingNFTs: boolean
@@ -103,6 +111,7 @@ export default class NFTsService extends BaseService<Events> {
103111
if (accountsToFetch.length) {
104112
await this.fetchCollections(accountsToFetch)
105113
await this.fetchPOAPs(accountsToFetch)
114+
await this.fetchCampaignNFTs(accountsToFetch)
106115
}
107116
}
108117

@@ -118,6 +127,7 @@ export default class NFTsService extends BaseService<Events> {
118127
await this.fetchCollections(accountsToFetch) // refetch only if there are some transfers
119128
}
120129
await this.fetchPOAPs(accountsToFetch)
130+
await this.fetchCampaignNFTs(accountsToFetch)
121131
}
122132

123133
async fetchCollections(accounts: AddressOnNetwork[]): Promise<void> {
@@ -172,6 +182,59 @@ export default class NFTsService extends BaseService<Events> {
172182
)
173183
}
174184

185+
async fetchCampaignNFTs(accounts: AddressOnNetwork[]): Promise<void> {
186+
if (isDisabled("SUPPORT_MEZO_NETWORK")) {
187+
return
188+
}
189+
190+
const provider = this.chainService.providerForNetworkOrThrow(MEZO_TESTNET)
191+
const contract = new Contract(
192+
NFT_CONTRACT_ADDRESS,
193+
[
194+
"function balanceOf(address, uint256) view returns (uint256)",
195+
"function uri(uint256) view returns (string)",
196+
],
197+
provider,
198+
)
199+
200+
await Promise.allSettled(
201+
accounts
202+
.filter(({ network }) => sameNetwork(network, MEZO_TESTNET))
203+
.map(async (account) => {
204+
const { address } = account
205+
206+
const hasTahoNFT =
207+
(await contract.callStatic.balanceOf(address, 1)) > 0n
208+
209+
const nfts: NFT[] = []
210+
211+
if (hasTahoNFT) {
212+
const metadataURI = await contract.callStatic.uri(1)
213+
214+
const details = await fetchJson(metadataURI)
215+
216+
nfts.push({
217+
collectionID: NFT_COLLECTION_ID,
218+
id: `CAMPAIGN.${contract.address}.${address}`,
219+
previewURL: details.image,
220+
thumbnailURL: details.image,
221+
name: details.name,
222+
description: details.description,
223+
tokenId: "1",
224+
contract: contract.address,
225+
owner: address,
226+
isBadge: false,
227+
network: MEZO_TESTNET,
228+
attributes: [],
229+
rarity: {},
230+
})
231+
232+
await this.updateSavedNFTs(NFT_COLLECTION_ID, account, nfts, {})
233+
}
234+
}),
235+
)
236+
}
237+
175238
async fetchNFTsFromDatabase(
176239
collectionID: string,
177240
account: AddressOnNetwork,
@@ -224,7 +287,10 @@ export default class NFTsService extends BaseService<Events> {
224287

225288
let updatedCollection: NFTCollection | undefined
226289

227-
if (collectionID === POAP_COLLECTION_ID) {
290+
if (
291+
collectionID === POAP_COLLECTION_ID ||
292+
collectionID.startsWith("campaign::")
293+
) {
228294
// update number of poaps
229295
updatedCollection = await this.db.updateCollectionData(
230296
collectionID,

ui/components/Wallet/Banner/WalletCampaignBanner.tsx

+17-4
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,11 @@ import MEZO_CAMPAIGN, {
66
MezoClaimStatus,
77
} from "@tallyho/tally-background/services/campaign/matsnet-nft"
88
import { assertUnreachable } from "@tallyho/tally-background/lib/utils/type-guards"
9-
import { markDismissableItemAsShown } from "@tallyho/tally-background/redux-slices/ui"
9+
import { AnalyticsEvent } from "@tallyho/tally-background/lib/posthog"
10+
import {
11+
markDismissableItemAsShown,
12+
sendEvent,
13+
} from "@tallyho/tally-background/redux-slices/ui"
1014

1115
import SharedButton from "../../Shared/SharedButton"
1216
import SharedIcon from "../../Shared/SharedIcon"
@@ -45,13 +49,22 @@ export default function MezoWalletCampaignBanner({
4549
browser.permissions.request({ permissions: ["notifications"] })
4650
switch (state) {
4751
case "eligible":
48-
browser.tabs.create({ url: "https://mezo.org/matsnet/borrow" })
52+
dispatch(sendEvent(AnalyticsEvent.CAMPAIGN_MEZO_NFT_ELIGIBLE_BANNER))
53+
browser.tabs.create({
54+
url: "https://mezo.org/matsnet/borrow?src=taho-claim-sats-banner",
55+
})
4956
break
5057
case "can-borrow":
51-
browser.tabs.create({ url: "https://mezo.org/matsnet/borrow" })
58+
dispatch(sendEvent(AnalyticsEvent.CAMPAIGN_MEZO_NFT_BORROW_BANNER))
59+
browser.tabs.create({
60+
url: "https://mezo.org/matsnet/borrow?src=taho-borrow-banner",
61+
})
5262
break
5363
case "can-claim-nft":
54-
browser.tabs.create({ url: "https://mezo.org/matsnet/store" })
64+
dispatch(sendEvent(AnalyticsEvent.CAMPAIGN_MEZO_NFT_CLAIM_NFT_BANNER))
65+
browser.tabs.create({
66+
url: "https://mezo.org/matsnet/store?src=taho-claim-nft-banner",
67+
})
5568
break
5669
default:
5770
assertUnreachable(state)

0 commit comments

Comments
 (0)