Skip to content

Commit

Permalink
Referrals
Browse files Browse the repository at this point in the history
  • Loading branch information
bone-house committed Sep 10, 2024
1 parent 5be2b56 commit cca33c4
Show file tree
Hide file tree
Showing 7 changed files with 181 additions and 678 deletions.
13 changes: 6 additions & 7 deletions apps/platform/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { ConnectionProvider, WalletProvider } from '@solana/wallet-adapter-react
import { WalletModalProvider } from '@solana/wallet-adapter-react-ui'
import '@solana/wallet-adapter-react-ui/styles.css'
import { PhantomWalletAdapter, SolflareWalletAdapter } from '@solana/wallet-adapter-wallets'
import { GambaPlatformProvider, ReferralProvider, TokenMetaProvider } from 'gamba-react-ui-v2'
import { GambaPlatformProvider, TokenMetaProvider } from 'gamba-react-ui-v2'
import { GambaProvider, SendTransactionProvider } from 'gamba-react-v2'
import React from 'react'
import ReactDOM from 'react-dom/client'
Expand Down Expand Up @@ -41,13 +41,12 @@ function Root() {
defaultCreatorFee={PLATFORM_CREATOR_FEE}
defaultJackpotFee={PLATFORM_JACKPOT_FEE}
defaultPool={DEFAULT_POOL}
referral={{
fee: PLATFORM_REFERRAL_FEE,
prefix: 'code',
}}
>
<ReferralProvider
prefix="code"
fee={PLATFORM_REFERRAL_FEE}
>
<App />
</ReferralProvider>
<App />
</GambaPlatformProvider>
</GambaProvider>
</SendTransactionProvider>
Expand Down
14 changes: 8 additions & 6 deletions apps/platform/src/sections/UserButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,10 @@ function UserModal() {
}
}

const revokeInvite = async () => {
const removeInvite = async () => {
try {
setRemoving(true)
await referral.removeReferral()
await referral.removeInvite()
} finally {
setRemoving(false)
}
Expand All @@ -51,13 +51,15 @@ function UserModal() {
Share your link with new users to earn {(PLATFORM_REFERRAL_FEE * 100)}% every time they play on this platform.
</div>
</div>
{PLATFORM_ALLOW_REFERRER_REMOVAL && referral.recipient && (
{PLATFORM_ALLOW_REFERRER_REMOVAL && referral.referrerAddress && (
<div style={{ display: 'flex', gap: '10px', flexDirection: 'column', width: '100%' }}>
<GambaUi.Button disabled={removing} onClick={revokeInvite}>
Revoke invite
<GambaUi.Button disabled={removing} onClick={removeInvite}>
Remove invite
</GambaUi.Button>
<div style={{ opacity: '.8', fontSize: '80%' }}>
You were invited by <a target="_blank" href={`https://solscan.io/account/${referral.recipient.toString()}`} rel="noreferrer">{referral.recipient.toString()}</a>
You were invited by <a target="_blank" href={`https://solscan.io/account/${referral.referrerAddress.toString()}`} rel="noreferrer">
{truncateString(referral.referrerAddress.toString(), 6, 6)}
</a>.
</div>
</div>
)}
Expand Down
17 changes: 13 additions & 4 deletions packages/react-ui/src/GambaPlatformProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { PublicKey } from '@solana/web3.js'
import { NATIVE_MINT } from 'gamba-core-v2'
import React from 'react'
import { PortalProvider } from './PortalContext'
import { ReferralProvider, ReferralProviderProps } from './referral/ReferralContext'

interface PlatformMeta {
name: string
Expand Down Expand Up @@ -34,10 +35,16 @@ interface GambaPlatformProviderProps extends React.PropsWithChildren {
defaultCreatorFee?: number
/** How much the player should pay in fees to play for the jackpot in every game. 0.001 = 0.1% */
defaultJackpotFee?: number
/** */
referral?: ReferralProviderProps
}

export function GambaPlatformProvider(props: GambaPlatformProviderProps) {
const { creator, children } = props
const {
creator,
children,
referral = { prefix: 'code', fee: 0.01, autoAccept: true },
} = props
const [selectedPool, setSelectedPool] = React.useState<PoolToken>(props.defaultPool ?? { token: NATIVE_MINT })
const [clientSeed, setClientSeed] = React.useState(String(Math.random() * 1e9 | 0))
const [defaultJackpotFee, setDefaultJackpotFee] = React.useState(props.defaultJackpotFee ?? 0.001)
Expand Down Expand Up @@ -74,9 +81,11 @@ export function GambaPlatformProvider(props: GambaPlatformProviderProps) {
defaultCreatorFee,
}}
>
<PortalProvider>
{children}
</PortalProvider>
<ReferralProvider {...referral}>
<PortalProvider>
{children}
</PortalProvider>
</ReferralProvider>
</GambaPlatformContext.Provider>
)
}
104 changes: 60 additions & 44 deletions packages/react-ui/src/referral/ReferralContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,32 +10,37 @@ import { getReferralAddressFromUrl } from './referralUtils'
const defaultPrefix = 'code'

export interface ReferralContext {
recipient: PublicKey | null
onChain: boolean
referrerAddress: PublicKey | null
isOnChain: boolean
prefix: string
referralStatus: 'local' | 'on-chain' | 'loading'
referralStatus: 'local' | 'on-chain' | 'fetching'
clearCache: () => void
setCache: (address: PublicKey, isOnChain?: boolean) => void
}

export const ReferralContext = createContext<ReferralContext>({
recipient: null,
onChain: false,
referrerAddress: null,
isOnChain: false,
prefix: defaultPrefix,
referralStatus: 'local',
clearCache: () => null,
setCache: () => null,
})

export interface ReferralProviderProps {
fee: number
prefix?: string
autoAccept?: boolean
/** localStorage or sessionStorage */
storage?: Storage
}

export function ReferralProvider({
fee,
prefix = defaultPrefix,
children,
storage = sessionStorage,
storage = localStorage,
autoAccept = true,
}: PropsWithChildren<ReferralProviderProps>) {
const wallet = useWallet()
const owner = useWalletAddress()
Expand Down Expand Up @@ -70,12 +75,13 @@ export function ReferralProvider({
let isCancelled = false

const handleReferral = async () => {
// 1. Check if we have an active invite URL (?ref=<address>)
// (Update localStorage and refresh if found)
// Check if we have an active invite URL (?ref=<address>)
const urlAddress = getReferralAddressFromUrl(prefix)
if (urlAddress) {
// Redirect and update localStorage cache.
sessionStorage.setItem('referral-new', urlAddress.toString())
if (autoAccept && urlAddress) {
// Store the referral address in "referral-new", since user might not have connected in this step
storage.setItem('referral-new', urlAddress.toString())

// Refresh
const url = new URL(window.location.href)
const params = url.searchParams
params.delete(prefix)
Expand All @@ -86,36 +92,35 @@ export function ReferralProvider({

if (!wallet.publicKey) {
setReferralCache({ address: null, isOnChain: false })
return
}

if (wallet.publicKey) {
// Fetch on-chain address determine if the transaction plugin needs to upsert a new one
setIsFetchingOnChain(true)
try {
const onChainAddress = await getOnChainAddress()
if (isCancelled) return
if (!onChainAddress) throw new Error
// Use on-chain address
setReferralCache({ address: onChainAddress, isOnChain: true })
} catch {
if (isCancelled) return
const storedReferralForAddress = getPublicKeyFromStorage('referral-' + wallet.publicKey.toString())
if (storedReferralForAddress) {
// Use local address
setReferralCache({ address: new PublicKey(storedReferralForAddress), isOnChain: false })
return
}
const newReferral = getPublicKeyFromStorage('referral-new')
if (newReferral && !newReferral.equals(wallet.publicKey)) {
// Update and use local address
setReferralCache({ address: new PublicKey(newReferral), isOnChain: false })
sessionStorage.setItem('referral-' + wallet.publicKey.toString(), newReferral.toString())
sessionStorage.removeItem('referral-new')
}
} finally {
if (!isCancelled)
setIsFetchingOnChain(false)
// Fetch on-chain address determine if the transaction plugin needs to upsert a new one
setIsFetchingOnChain(true)
try {
const onChainAddress = await getOnChainAddress()
if (isCancelled) return
if (!onChainAddress) throw new Error
// Use on-chain address
setReferralCache({ address: onChainAddress, isOnChain: true })
} catch {
if (isCancelled) return
const storedReferralForAddress = getPublicKeyFromStorage('referral-' + wallet.publicKey.toString())
if (storedReferralForAddress) {
// Use local address
setReferralCache({ address: storedReferralForAddress, isOnChain: false })
return
}
const newReferral = getPublicKeyFromStorage('referral-new')
if (newReferral && !newReferral.equals(wallet.publicKey)) {
// Update and use local address
setReferralCache({ address: newReferral, isOnChain: false })
storage.setItem('referral-' + wallet.publicKey.toString(), newReferral.toString())
storage.removeItem('referral-new')
}
} finally {
if (!isCancelled)
setIsFetchingOnChain(false)
}
}

Expand All @@ -125,6 +130,7 @@ export function ReferralProvider({
isCancelled = true
}
}, [
autoAccept,
gambaPlatformContext.platform.creator.toString(),
wallet.publicKey?.toString(),
prefix,
Expand All @@ -137,25 +143,35 @@ export function ReferralProvider({
referralCache.address,
!referralCache.isOnChain,
fee,
1,
),
)
}, [referralCache])
}, [fee, referralCache.address, referralCache.isOnChain])

const clearCache = () => {
if (wallet.publicKey) {
sessionStorage.removeItem('referral-' + wallet.publicKey.toString())
storage.removeItem('referral-' + wallet.publicKey.toString())
}
sessionStorage.removeItem('referral-new')
storage.removeItem('referral-new')
setReferralCache({ address: null, isOnChain: false })
}

const setCache = (address: PublicKey, isOnChain = false) => {
if (wallet.publicKey) {
storage.setItem('referral-' + wallet.publicKey.toString(), address.toString())
}
storage.setItem('referral-new', address.toString())
setReferralCache({ address: address, isOnChain })
}

return (
<ReferralContext.Provider value={{
prefix,
onChain: referralCache.isOnChain,
recipient: referralCache.address,
referralStatus: isFetchingOnChain ? 'loading' : referralCache.isOnChain ? 'on-chain' : 'local',
isOnChain: referralCache.isOnChain,
referrerAddress: referralCache.address,
referralStatus: isFetchingOnChain ? 'fetching' : referralCache.isOnChain ? 'on-chain' : 'local',
clearCache,
setCache,
}}>
{children}
</ReferralContext.Provider>
Expand Down
7 changes: 4 additions & 3 deletions packages/react-ui/src/referral/referralPlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@ import { createReferral } from './program'
export const makeReferralPlugin = (
recipient: PublicKey,
upsert: boolean,
feePercent = 0.01,
referralFee = 0.01,
creatorFeeDeduction = 1,
): GambaPlugin => async (input, context) => {
const instructions: TransactionInstruction[] = []
const tokenAmount = BigInt(Math.floor(input.wager * feePercent))
const tokenAmount = BigInt(Math.floor(input.wager * referralFee))

if (upsert) {
// Save the referral address on-chain
Expand Down Expand Up @@ -69,7 +70,7 @@ export const makeReferralPlugin = (
}

// Override creatorFee so that the player doesn't end up paying more
context.creatorFee = Math.max(0, context.creatorFee - feePercent)
context.creatorFee = Math.max(0, context.creatorFee - referralFee * creatorFeeDeduction)

return instructions
}
Loading

0 comments on commit cca33c4

Please sign in to comment.