Skip to content
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

feat(unlock-app): Support ENS in checkout builder for Referrer field #14834

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions unlock-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@
"deploy-fleek": "./scripts/deploy-fleek.sh",
"start": "yarn build && NODE_ENV=production next start",
"test": "UNLOCK_ENV=test vitest run --coverage --environment=jsdom",
"test:watch": "UNLOCK_ENV=test vitest --coverage --environment=jsdom -w",
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added for convenience, Let me know if this has to be reverted

"lint": "eslint",
"ci": "yarn test && yarn lint && yarn build"
},
Expand Down
112 changes: 111 additions & 1 deletion unlock-app/src/__tests__/utils/checkoutLockUtils.test.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import { PaywallConfigType } from '@unlock-protocol/core'
import {
lockKeysAvailable,
lockTickerSymbol,
userCanAffordKey,
formattedKeyPrice,
convertedKeyPrice,
getReferrers,
} from '../../utils/checkoutLockUtils'
import { it, describe, expect } from 'vitest'
import { it, describe, expect, vi } from 'vitest'

const lockAddress = '0x2B24bE6c9d5b70Ad53203AdB780681cd70603660'
const network = 5

describe('Checkout Lock Utils', () => {
describe('lockKeysAvailable', () => {
it('returns Unlimited if it has unlimited keys', () => {
Expand Down Expand Up @@ -228,4 +231,111 @@ describe('Checkout Lock Utils', () => {
)
})
})

describe('getReferrers', () => {
const recipients = [
'0x2B24bE6c9d5b70Ad53203AdB780681cd70603660',
'0x1234567890123456789012345678901234567891',
]
const lockAddress = '0x87dA72DC59674A17AD2154a25699246c51E25a57'
let paywallConfig: PaywallConfigType

beforeEach(() => {
paywallConfig = {
locks: {
'0x87dA72DC59674A17AD2154a25699246c51E25a57': {
referrer: '0x1234567890123456789012345678901234567890',
network: 11155111,
},
},
referrer: '0xE5Cd62AC8d2Ca2A62a04958f07Dd239c1Ffe1a9E',
}
vi.resetModules()
})

it('should return paywallConfig locks referrer', async () => {
expect.assertions(1)
expect(
await getReferrers(recipients, paywallConfig, lockAddress)
).toEqual([
'0x1234567890123456789012345678901234567890',
'0x1234567890123456789012345678901234567890',
])
})

it('should return paywallConfig referrer', async () => {
expect.assertions(1)
paywallConfig = {
...paywallConfig,
locks: {
'0x87dA72DC59674A17AD2154a25699246c51E25a57': {
referrer: '0x1234567890123',
},
},
}

expect(
await getReferrers(recipients, paywallConfig, lockAddress)
).toEqual([
'0xE5Cd62AC8d2Ca2A62a04958f07Dd239c1Ffe1a9E',
'0xE5Cd62AC8d2Ca2A62a04958f07Dd239c1Ffe1a9E',
])
})

it('should return recipients if referrers are not addresses', async () => {
expect.assertions(1)
paywallConfig = {
...paywallConfig,
locks: {
'0x87dA72DC59674A17AD2154a25699246c51E25a57': {
referrer: '0x1234567890123',
},
},
referrer: '0x62CcB13A72E6F991',
}

expect(
await getReferrers(recipients, paywallConfig, lockAddress)
).toEqual([
'0x2B24bE6c9d5b70Ad53203AdB780681cd70603660',
'0x1234567890123456789012345678901234567891',
])
})

it('should resolve for ens if the referrer is an ens address', async () => {
vi.doMock('../../utils/resolvers', () => {
return {
onResolveName: () =>
Promise.resolve({
address: 'ensAddressMock',
}),
}
})

vi.doMock('@unlock-protocol/ui', () => {
return {
isEns: () => Promise.resolve(true),
}
})

const { getReferrers } = await import('../../utils/checkoutLockUtils')
expect.assertions(1)
paywallConfig = {
...paywallConfig,
locks: {
'0x87dA72DC59674A17AD2154a25699246c51E25a57': {
referrer: '0x1234567890123',
},
},
referrer: 'test.eth',
}

expect(
await getReferrers(recipients, paywallConfig, lockAddress)
).toEqual(['ensAddressMock', 'ensAddressMock'])

vi.unmock('../../utils/resolvers')
vi.unmock('@unlock-protocol/ui')
})
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { loadStripe } from '@stripe/stripe-js'
import { useSelector } from '@xstate/react'
import { PoweredByUnlock } from '../../PoweredByUnlock'
import { Pricing } from '../../Lock'
import { getReferrer, lockTickerSymbol } from '~/utils/checkoutLockUtils'
import { lockTickerSymbol } from '~/utils/checkoutLockUtils'
import { Lock } from '~/unlockTypes'
import { RiErrorWarningFill as ErrorIcon } from 'react-icons/ri'
import { usePurchase } from '~/hooks/usePurchase'
Expand All @@ -22,6 +22,7 @@ import { useGetLockSettings } from '~/hooks/useLockSettings'
import { getCurrencySymbol } from '~/utils/currency'
import Disconnect from '../Disconnect'
import { ToastHelper } from '~/components/helpers/toast.helper'
import { useGetReferrers } from '~/hooks/useGetReferrers'

interface Props {
checkoutService: CheckoutService
Expand Down Expand Up @@ -171,14 +172,17 @@ export function ConfirmCard({ checkoutService, onConfirmed, onError }: Props) {
network: lock!.network,
purchaseData: purchaseData || [],
})
const { data: referrers } = useGetReferrers(
recipients,
paywallConfig,
lockAddress
)

const { mutateAsync: capturePayment } = useCapturePayment({
network: lock!.network,
lockAddress: lock!.address,
data: purchaseData,
referrers: recipients.map((recipient: string) =>
getReferrer(recipient, paywallConfig, lockAddress)
),
referrers,
recipients,
purchaseType: renew ? 'extend' : 'purchase',
})
Expand All @@ -191,10 +195,6 @@ export function ConfirmCard({ checkoutService, onConfirmed, onError }: Props) {
const onConfirmCard = async () => {
setIsConfirming(true)
try {
const referrers: string[] = recipients.map((recipient) => {
return getReferrer(recipient, paywallConfig, lockAddress)
})

const stripeIntent = await createPurchaseIntent({
pricing: totalPricing!.total * 100, //
// @ts-expect-error - generated types don't narrow down to the right type
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { Fragment, useCallback, useState } from 'react'
import { useSelector } from '@xstate/react'
import { PoweredByUnlock } from '../../PoweredByUnlock'
import { Pricing } from '../../Lock'
import { getReferrer, lockTickerSymbol } from '~/utils/checkoutLockUtils'
import { lockTickerSymbol } from '~/utils/checkoutLockUtils'
import { Lock } from '~/unlockTypes'
import { RiErrorWarningFill as ErrorIcon } from 'react-icons/ri'
import { usePricing } from '~/hooks/usePricing'
Expand All @@ -19,6 +19,7 @@ import { TransactionAnimation } from '../../Shell'
import { config } from '~/config/app'
import { useGetTokenIdForOwner } from '~/hooks/useGetTokenIdForOwner'
import Disconnect from '../Disconnect'
import { useGetReferrers } from '~/hooks/useGetReferrers'

interface Props {
checkoutService: CheckoutService
Expand Down Expand Up @@ -46,7 +47,6 @@ export function ConfirmCrossmint({
useSelector(checkoutService, (state) => state.context)
const [isConfirming, setIsConfirming] = useState(false)
const [quote, setQuote] = useState<CrossmintQuote | null>(null)

const crossmintEnv = config.env === 'prod' ? 'production' : 'staging'

const {
Expand All @@ -73,6 +73,12 @@ export function ConfirmCrossmint({
environment: crossmintEnv,
})

const { data: referrers } = useGetReferrers(
recipients,
paywallConfig,
lock!.address
)

// Handling payment events
const onCrossmintPaymentEvent = useCallback(
(paymentEvent: any) => {
Expand Down Expand Up @@ -133,10 +139,6 @@ export function ConfirmCrossmint({
crossmintLoading ||
(!tokenId && renew))

const referrers: string[] = recipients.map((recipient) => {
return getReferrer(recipient, paywallConfig, lock!.address)
})

const values = pricingData
? [
ethers.parseUnits(
Expand Down Expand Up @@ -170,7 +172,7 @@ export function ConfirmCrossmint({
crossmintConfig.mintConfig = {
totalPrice: pricingData?.total.toString(),
_values: values,
_referrers: referrers,
_referrers: referrers?.[0],
_keyManagers: keyManagers || recipients,
_data: purchaseData,
}
Expand All @@ -180,7 +182,7 @@ export function ConfirmCrossmint({
totalPrice: pricingData?.total.toString(),
_tokenId: tokenId,
_value: values[0],
_referrer: referrers[0],
_referrer: referrers?.[0],
_data: purchaseData ? purchaseData[0] : '',
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { getAccountTokenBalance } from '~/hooks/useAccount'
import { useSelector } from '@xstate/react'
import { useWeb3Service } from '~/utils/withWeb3Service'
import { Pricing } from '../../Lock'
import { getReferrer, lockTickerSymbol } from '~/utils/checkoutLockUtils'
import { getReferrers, lockTickerSymbol } from '~/utils/checkoutLockUtils'
import { Lock } from '~/unlockTypes'
import ReCaptcha from 'react-google-recaptcha'
import { RiErrorWarningFill as ErrorIcon } from 'react-icons/ri'
Expand Down Expand Up @@ -146,10 +146,6 @@ export function ConfirmCrypto({
pricingData?.prices.map((item) => item.amount.toString()) ||
new Array(recipients!.length).fill(keyPrice)

const referrers: string[] = recipients.map((recipient) => {
return getReferrer(recipient, paywallConfig, lockAddress)
})

const onErrorCallback = (error: Error | null, hash: string | null) => {
setIsConfirming(false)
if (error) {
Expand All @@ -165,11 +161,16 @@ export function ConfirmCrypto({

const walletService = await getWalletService(lockNetwork)
if (renew) {
const referrers = await getReferrers(
[account!],
paywallConfig,
lockAddress
)
await walletService.extendKey(
{
lockAddress,
owner: recipients?.[0],
referrer: getReferrer(account!, paywallConfig, lockAddress),
referrer: referrers[0],
data: purchaseData?.[0],
recurringPayment: recurringPayments
? recurringPayments[0]
Expand All @@ -179,6 +180,11 @@ export function ConfirmCrypto({
onErrorCallback
)
} else {
const referrers = await getReferrers(
recipients,
paywallConfig,
lockAddress
)
await walletService.purchaseKeys(
{
lockAddress,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,6 @@ export function Select({ checkoutService }: Props) {
props.network || paywallConfig.network || 1

const lockData = await web3Service.getLock(lock, networkId)

let price

if (account) {
Expand Down
5 changes: 3 additions & 2 deletions unlock-app/src/components/interface/keychain/Extend.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import { config } from '~/config/app'
import { useWeb3Service } from '~/utils/withWeb3Service'
import { Placeholder } from '@unlock-protocol/ui'
import { Key } from '~/hooks/useKeys'
import { getReferrer } from '~/utils/checkoutLockUtils'
import { getReferrers } from '~/utils/checkoutLockUtils'

const ExtendMembershipPlaceholder = () => {
return (
Expand Down Expand Up @@ -121,10 +121,11 @@ export const ExtendMembershipModal = ({
walletService.signer
)
} else {
const referrers = await getReferrers([account!])
await walletService.extendKey({
lockAddress: lock?.address,
tokenId: ownedKey.tokenId,
referrer: getReferrer(account!),
referrer: referrers[0],
recurringPayment: renewal ? renewal : undefined,
totalApproval: unlimited ? MAX_UINT : undefined,
})
Expand Down
Loading
Loading