diff --git a/apps/daimo-web/public/assets/deposit/usdt-tron.png b/apps/daimo-web/public/assets/deposit/usdt-tron.png new file mode 100644 index 000000000..05eb118bb Binary files /dev/null and b/apps/daimo-web/public/assets/deposit/usdt-tron.png differ diff --git a/packages/daimo-api/src/api/featureFlag.ts b/packages/daimo-api/src/api/featureFlag.ts index 07b46b7af..e8f61a89e 100644 --- a/packages/daimo-api/src/api/featureFlag.ts +++ b/packages/daimo-api/src/api/featureFlag.ts @@ -1,7 +1,7 @@ import { EAccount } from "@daimo/common"; export class FeatFlag { - public static landline(account: EAccount & { name: string }) { + public static tronramp(account: EAccount) { return [ "dcposch", "klee", @@ -10,6 +10,8 @@ export class FeatFlag { "hanna", "sfl", "sbg", - ].includes(account.name); + "ansgar", + "liam", + ].includes(account.name || ""); } } diff --git a/packages/daimo-api/src/api/getAccountHistory.ts b/packages/daimo-api/src/api/getAccountHistory.ts index 148f68b77..5f7ec5105 100644 --- a/packages/daimo-api/src/api/getAccountHistory.ts +++ b/packages/daimo-api/src/api/getAccountHistory.ts @@ -14,6 +14,7 @@ import { TransferClog, appStoreLinks, assert, + assertNotNull, daimoDomainAddress, formatDaimoLink, getLandlineAccountName, @@ -164,9 +165,6 @@ export async function getAccountHistory( // Prefetch info required to send operations > fast at time of sending. const chainGasConstants = await paymaster.calculateChainGasConstants(eAcc); - // Prefetch info required to deposit to your Daimo account. - const recommendedExchanges = fetchRecommendedExchanges(eAcc, lang); - // Get linked accounts const linkedAccounts = profileCache.getLinkedAccounts(address); const inviteLinkStatus = inviteCode @@ -205,22 +203,25 @@ export async function getAccountHistory( let landlineSessionURL = ""; let landlineAccounts: LandlineAccount[] = []; - const showLandline = FeatFlag.landline(eAcc); - if (getEnvApi().LANDLINE_API_URL && showLandline) { - const landlineSessionKey = (await getLandlineSession(address)).key; + // Landline supported starting in 1.9.11 + let landlineSessionKey: string | undefined; + if ( + getEnvApi().LANDLINE_API_URL && + appVersion && + semver.gte(appVersion, "1.9.11") + ) { + const daimoAddress = address; + const llSession = await getLandlineSession({ daimoAddress }, ctx); + landlineSessionKey = llSession.key; landlineSessionURL = getLandlineURL(address, landlineSessionKey); - landlineAccounts = await getLandlineAccounts(address); - // Support for displaying landline transfers in the mobile app was added - // in version 1.9.7 and doesn't support backcompat. - // Only add landline transfers if the app version is > 1.9.6 - if (appVersion && semver.gt(appVersion, "1.9.6")) { - const landlineTransfers = await getLandlineTransfers(address); - transferClogs = addLandlineTransfers( - landlineTransfers, - transferClogs, - chainConfig.daimoChain - ); - } + landlineAccounts = await getLandlineAccounts({ daimoAddress }, ctx); + + const landlineTransfers = await getLandlineTransfers({ daimoAddress }, ctx); + transferClogs = addLandlineTransfers( + landlineTransfers, + transferClogs, + chainConfig.daimoChain + ); } // Get named accounts @@ -230,6 +231,13 @@ export async function getAccountHistory( nameReg ); + // Prefetch info required to deposit to your Daimo account. + const recommendedExchanges = await fetchRecommendedExchanges({ + account: eAcc, + language: lang, + landlineSessionKey, + }); + const ret: AccountHistoryResult = { address, sinceBlockNum, @@ -357,13 +365,18 @@ function getCoinbaseURL(account: EAccount) { }); } -function fetchRecommendedExchanges( - account: EAccount, - lang?: string -): RecommendedExchange[] { - const i18 = i18n(lang).recommendedExchange; - - return [ +function fetchRecommendedExchanges({ + account, + language, + landlineSessionKey, +}: { + account: EAccount; + language?: string; + landlineSessionKey?: string; +}): RecommendedExchange[] { + const i18 = i18n(landlineSessionKey).recommendedExchange; + + const ret = [ { cta: i18.bridge.cta(), title: i18.bridge.title(), @@ -380,11 +393,24 @@ function fetchRecommendedExchanges( }, // 2 is Binance, loaded client-side on demand. { - title: i18.ramp.title(), cta: i18.ramp.cta(), + title: i18.ramp.title(), url: getRampNetworkURL(account), logo: `${daimoDomainAddress}/assets/deposit/usdc.png`, - sortId: 3, + sortId: 4, }, ]; + + if (landlineSessionKey != null && FeatFlag.tronramp(account)) { + const llHost = assertNotNull(getEnvApi().LANDLINE_DOMAIN); + ret.push({ + cta: `Preview ยท Tron Deposit`, + title: `Receive USDT TRC-20`, + url: `${llHost}/tron/${account.addr}/${landlineSessionKey}`, + logo: `${daimoDomainAddress}/assets/deposit/usdt-tron.png`, + sortId: 3, + }); + } + + return ret; } diff --git a/packages/daimo-api/src/landline/connector.ts b/packages/daimo-api/src/landline/connector.ts index adf9518f0..50917d7d9 100644 --- a/packages/daimo-api/src/landline/connector.ts +++ b/packages/daimo-api/src/landline/connector.ts @@ -3,6 +3,7 @@ import { Address } from "viem"; import { landlineTrpc } from "./trpc"; import { getEnvApi } from "../env"; +import { TrpcRequestContext } from "../server/trpc"; export interface LandlineSessionKey { key: string; @@ -21,15 +22,25 @@ export function getLandlineURL(daimoAddress: string, sessionKey: string) { } export async function getLandlineSession( - daimoAddress: Address + { + daimoAddress, + }: { + daimoAddress: Address; + }, + context: TrpcRequestContext ): Promise { console.log(`[LANDLINE] getting session key for ${daimoAddress}`); try { // @ts-ignore - const sessionKey = await landlineTrpc.getOrCreateSessionKey.mutate({ - daimoAddress, - }); + const sessionKey = await landlineTrpc.getOrCreateSessionKey.mutate( + { + daimoAddress, + }, + { + context, + } + ); console.log(`[LANDLINE] got session key for ${daimoAddress}`); return sessionKey; } catch (err: any) { @@ -43,16 +54,26 @@ export async function getLandlineSession( } export async function getLandlineAccounts( - daimoAddress: Address + { + daimoAddress, + }: { + daimoAddress: Address; + }, + context: TrpcRequestContext ): Promise { console.log(`[LANDLINE] getting external accounts for ${daimoAddress}`); try { const landlineAccounts = // @ts-ignore - await landlineTrpc.getExternalAccountsTransferInfo.query({ - daimoAddress, - }); + await landlineTrpc.getExternalAccountsTransferInfo.query( + { + daimoAddress, + }, + { + context, + } + ); console.log(`[LANDLINE] got external accounts for ${daimoAddress}`); // TODO: change to number. Currently a string for backcompat return landlineAccounts.map((account: any) => ({ @@ -70,28 +91,47 @@ export async function getLandlineAccounts( } export async function getLandlineTransfers( - daimoAddress: Address, - createdAfter?: number + { + daimoAddress, + createdAfterS, + }: { + daimoAddress: Address; + createdAfterS?: number; + }, + context: TrpcRequestContext ): Promise { // Convert createdAfter from Unix seconds to a Date object if it's provided - const createdAfterDate = createdAfter - ? new Date(createdAfter * 1000) + const createdAfter = createdAfterS + ? new Date(createdAfterS * 1000) : undefined; const transfers = // @ts-ignore - await landlineTrpc.getAllLandlineTransfers.query({ - daimoAddress, - createdAfter: createdAfterDate, - }); + await landlineTrpc.getAllLandlineTransfers.query( + { + daimoAddress, + createdAfter, + }, + { + context, + } + ); return transfers; } export async function landlineDeposit( - daimoAddress: Address, - landlineAccountUuid: string, - amount: string, - memo: string | undefined + { + daimoAddress, + landlineAccountUuid, + amount, + memo, + }: { + daimoAddress: Address; + landlineAccountUuid: string; + amount: string; + memo?: string; + }, + context: TrpcRequestContext ): Promise { console.log("[LANDLINE] making deposit", { daimoAddress, @@ -102,12 +142,17 @@ export async function landlineDeposit( try { // @ts-ignore - const depositResponse = await landlineTrpc.deposit.mutate({ - daimoAddress, - landlineAccountUuid, - amount, - memo, - }); + const depositResponse = await landlineTrpc.deposit.mutate( + { + daimoAddress, + landlineAccountUuid, + amount, + memo, + }, + { + context, + } + ); console.log( `[LANDLINE] created deposit for ${daimoAddress}, landlineAccountUuid: ${landlineAccountUuid}, amount: ${amount}, memo: ${memo}`, depositResponse @@ -133,18 +178,26 @@ export type ShouldFastFinishResponse = { * @param amount amount in the units of the destination token (USDC) * @returns */ -export async function validateLandlineDeposit(args: { - daimoAddress: Address; - amount: string; -}) { +export async function validateLandlineDeposit( + args: { + daimoAddress: Address; + amount: string; + }, + context: TrpcRequestContext +) { console.log(`[LANDLINE] validating deposit ${debugJson(args)}`); try { const response: ShouldFastFinishResponse = // @ts-ignore - await landlineTrpc.shouldFastDeposit.query({ - daimoAddress: args.daimoAddress, - amount: args.amount, - }); + await landlineTrpc.shouldFastDeposit.query( + { + daimoAddress: args.daimoAddress, + amount: args.amount, + }, + { + context, + } + ); console.log( `[LANDLINE] validateLandlineDeposit ${debugJson({ args, response })}` ); diff --git a/packages/daimo-api/src/landline/trpc.ts b/packages/daimo-api/src/landline/trpc.ts index e2a48c046..1fc139082 100644 --- a/packages/daimo-api/src/landline/trpc.ts +++ b/packages/daimo-api/src/landline/trpc.ts @@ -1,19 +1,22 @@ import { createTRPCClient, httpBatchLink } from "@trpc/client"; -const createHeaders = () => { - const headers: Record = { - "x-api-key": process.env.LANDLINE_API_KEY || "", - }; +import { getEnvApi } from "../env"; +import { TrpcRequestContext } from "../server/trpc"; - return headers; -}; +const env = getEnvApi(); // TODO(andrew): Add type to createTRPCClient export const landlineTrpc = createTRPCClient({ links: [ httpBatchLink({ url: process.env.LANDLINE_API_URL || "", - headers: createHeaders(), + headers: (o) => { + const context = o.opList[0].context as TrpcRequestContext; + return { + "x-api-key": env.LANDLINE_API_KEY, + "x-forwarded-for": context.ipAddr, + }; + }, }), ], }); diff --git a/packages/daimo-api/src/server/router.ts b/packages/daimo-api/src/server/router.ts index 9bab090d7..71f5b3d28 100644 --- a/packages/daimo-api/src/server/router.ts +++ b/packages/daimo-api/src/server/router.ts @@ -715,10 +715,13 @@ export function createRouter( assert(action.type === "landlineDeposit", "Invalid action type"); const response = await landlineDeposit( - daimoAddress, - action.landlineAccountUuid, - action.amount, - action.memo + { + daimoAddress, + landlineAccountUuid: action.landlineAccountUuid, + amount: action.amount, + memo: action.memo, + }, + opts.ctx ); return response; @@ -734,7 +737,7 @@ export function createRouter( .query(async (opts) => { const { daimoAddress, amount } = opts.input; const response: ShouldFastFinishResponse = - await validateLandlineDeposit({ daimoAddress, amount }); + await validateLandlineDeposit({ daimoAddress, amount }, opts.ctx); return response; }), diff --git a/packages/daimo-api/src/server/telemetry.ts b/packages/daimo-api/src/server/telemetry.ts index 401232832..7af325480 100644 --- a/packages/daimo-api/src/server/telemetry.ts +++ b/packages/daimo-api/src/server/telemetry.ts @@ -150,5 +150,5 @@ export class Telemetry { function getIpCountry(ipAddr: string) { const ipGeo = geoIP.lookup(ipAddr); - return ipGeo?.country || "Atlantis"; + return ipGeo?.country || "Unknown"; }