Skip to content

Commit fb446bd

Browse files
[SDK] feat: Add transfer success callback (#6415)
1 parent 24063af commit fb446bd

File tree

8 files changed

+138
-17
lines changed

8 files changed

+138
-17
lines changed

.changeset/funny-eggs-hear.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"thirdweb": patch
3+
---
4+
5+
emit onPurchaseSuccess for wallet to wallet transfer flow before execution

apps/playground-web/src/components/in-app-wallet/ecosystem.tsx

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,21 @@ import type { ConnectButtonProps } from "thirdweb/react";
33
import { ecosystemWallet } from "thirdweb/wallets";
44
import { StyledConnectEmbed } from "../styled-connect-embed";
55

6-
const getEcosystem = () => {
6+
const getEcosystemWallet = () => {
77
if (
88
process.env.NEXT_PUBLIC_IN_APP_WALLET_URL?.endsWith(".thirdweb-dev.com")
99
) {
1010
// dev ecosystem
11-
return "ecosystem.catlovers";
11+
return ecosystemWallet("ecosystem.catlovers");
1212
}
1313
// prod ecosystem
14-
return "ecosystem.thirdweb-engs";
14+
return ecosystemWallet("ecosystem.thirdweb-engs", {
15+
partnerId: "50fd8850-3c6c-48dd-969c-622f88b52d95",
16+
});
1517
};
1618

1719
export function EcosystemConnectEmbed(
1820
props?: Omit<ConnectButtonProps, "client" | "theme">,
1921
) {
20-
return (
21-
<StyledConnectEmbed
22-
{...props}
23-
wallets={[ecosystemWallet(getEcosystem())]}
24-
/>
25-
);
22+
return <StyledConnectEmbed {...props} wallets={[getEcosystemWallet()]} />;
2623
}

apps/playground-web/src/components/pay/transaction-button.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ export function PayTransactionPreview() {
3535
const { theme } = useTheme();
3636
const { data: nft } = useReadContract(getNFT, {
3737
contract: nftContract,
38-
tokenId: 1n,
38+
tokenId: 2n,
3939
});
4040

4141
return (
@@ -51,7 +51,7 @@ export function PayTransactionPreview() {
5151
transaction: claimTo({
5252
contract: nftContract,
5353
quantity: 1n,
54-
tokenId: 1n,
54+
tokenId: 2n,
5555
to: account?.address || "",
5656
}),
5757
metadata: nft?.metadata,

packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/swap/TransferConfirmationScreen.tsx

Lines changed: 116 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,18 @@ import type { ThirdwebClient } from "../../../../../../../client/client.js";
66
import { NATIVE_TOKEN_ADDRESS } from "../../../../../../../constants/addresses.js";
77
import { getContract } from "../../../../../../../contract/contract.js";
88
import { allowance } from "../../../../../../../extensions/erc20/__generated__/IERC20/read/allowance.js";
9+
import {
10+
type GetCurrencyMetadataResult,
11+
getCurrencyMetadata,
12+
} from "../../../../../../../extensions/erc20/read/getCurrencyMetadata.js";
913
import { approve } from "../../../../../../../extensions/erc20/write/approve.js";
1014
import { transfer } from "../../../../../../../extensions/erc20/write/transfer.js";
15+
import type { BuyWithCryptoStatus } from "../../../../../../../pay/buyWithCrypto/getStatus.js";
1116
import { getBuyWithCryptoTransfer } from "../../../../../../../pay/buyWithCrypto/getTransfer.js";
1217
import { sendAndConfirmTransaction } from "../../../../../../../transaction/actions/send-and-confirm-transaction.js";
1318
import { sendTransaction } from "../../../../../../../transaction/actions/send-transaction.js";
1419
import { prepareTransaction } from "../../../../../../../transaction/prepare-transaction.js";
20+
import type { TransactionReceipt } from "../../../../../../../transaction/types.js";
1521
import type { Address } from "../../../../../../../utils/address.js";
1622
import { toWei } from "../../../../../../../utils/units.js";
1723
import { iconSize } from "../../../../../../core/design-system/index.js";
@@ -42,6 +48,7 @@ type TransferConfirmationScreenProps = {
4248
tokenAmount: string;
4349
transactionMode?: boolean;
4450
payOptions?: PayUIOptions;
51+
onSuccess: ((status: BuyWithCryptoStatus) => void) | undefined;
4552
};
4653

4754
export function TransferConfirmationScreen(
@@ -202,10 +209,33 @@ export function TransferConfirmationScreen(
202209
to: receiverAddress,
203210
amount: tokenAmount,
204211
});
205-
await sendAndConfirmTransaction({
206-
account: props.payer.account,
207-
transaction,
208-
});
212+
const [txResult, tokenMetadata] = await Promise.all([
213+
sendAndConfirmTransaction({
214+
account: props.payer.account,
215+
transaction,
216+
}),
217+
getCurrencyMetadata({
218+
contract: getContract({
219+
address: isNativeToken(token)
220+
? NATIVE_TOKEN_ADDRESS
221+
: token.address,
222+
chain: chain,
223+
client: client,
224+
}),
225+
}),
226+
]);
227+
// its the last step before the transaction, so propagate onPurchaseSuccess here
228+
props.onSuccess?.(
229+
transferBuyWithCryptoQuote({
230+
token,
231+
chain,
232+
tokenMetadata,
233+
tokenAmount,
234+
fromAddress: payer.account.address,
235+
toAddress: receiverAddress,
236+
transaction: txResult,
237+
}),
238+
);
209239
// switch to execute step
210240
setStep("execute");
211241
setStatus({ id: "idle" });
@@ -299,3 +329,85 @@ export function TransferConfirmationScreen(
299329
</Container>
300330
);
301331
}
332+
333+
function transferBuyWithCryptoQuote(args: {
334+
token: ERC20OrNativeToken;
335+
chain: Chain;
336+
tokenMetadata: GetCurrencyMetadataResult;
337+
tokenAmount: string;
338+
fromAddress: string;
339+
toAddress: string;
340+
transaction: TransactionReceipt;
341+
}): BuyWithCryptoStatus {
342+
const {
343+
token,
344+
chain,
345+
tokenMetadata,
346+
tokenAmount,
347+
fromAddress,
348+
toAddress,
349+
transaction,
350+
} = args;
351+
return {
352+
status: "COMPLETED",
353+
subStatus: "SUCCESS",
354+
swapType: "TRANSFER",
355+
quote: {
356+
createdAt: new Date().toISOString(),
357+
fromToken: {
358+
chainId: chain.id,
359+
tokenAddress: isNativeToken(token)
360+
? NATIVE_TOKEN_ADDRESS
361+
: token.address,
362+
decimals: tokenMetadata.decimals,
363+
symbol: tokenMetadata.symbol,
364+
name: tokenMetadata.name,
365+
priceUSDCents: 0,
366+
},
367+
toToken: {
368+
chainId: chain.id,
369+
tokenAddress: isNativeToken(token)
370+
? NATIVE_TOKEN_ADDRESS
371+
: token.address,
372+
decimals: tokenMetadata.decimals,
373+
symbol: tokenMetadata.symbol,
374+
name: tokenMetadata.name,
375+
priceUSDCents: 0,
376+
},
377+
fromAmountWei: toWei(tokenAmount).toString(),
378+
fromAmount: tokenAmount,
379+
toAmountWei: toWei(tokenAmount).toString(),
380+
toAmount: tokenAmount,
381+
toAmountMin: tokenAmount,
382+
toAmountMinWei: toWei(tokenAmount).toString(),
383+
estimated: {
384+
feesUSDCents: 0,
385+
gasCostUSDCents: 0,
386+
slippageBPS: 0,
387+
toAmountMinUSDCents: 0,
388+
toAmountUSDCents: 0,
389+
fromAmountUSDCents: 0,
390+
durationSeconds: 0,
391+
},
392+
},
393+
fromAddress,
394+
toAddress,
395+
source: {
396+
transactionHash: transaction.transactionHash,
397+
amount: tokenAmount,
398+
amountWei: toWei(tokenAmount).toString(),
399+
amountUSDCents: 0,
400+
completedAt: new Date().toISOString(),
401+
token: {
402+
chainId: chain.id,
403+
tokenAddress: isNativeToken(token)
404+
? NATIVE_TOKEN_ADDRESS
405+
: token.address,
406+
decimals: tokenMetadata.decimals,
407+
symbol: tokenMetadata.symbol,
408+
name: tokenMetadata.name,
409+
priceUSDCents: 0,
410+
},
411+
},
412+
};
413+
}

packages/thirdweb/src/wallets/smart/lib/bundler.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,7 @@ export async function estimateUserOpGasCost(args: {
179179
client: args.client,
180180
}),
181181
),
182+
waitForDeployment: false,
182183
});
183184

184185
let gasLimit = 0n;

packages/thirdweb/src/wallets/smart/lib/userop.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -321,7 +321,9 @@ async function populateUserOp_v0_7(args: {
321321
createAccountOverride: overrides?.createAccount,
322322
}),
323323
);
324-
markAccountDeploying(accountContract);
324+
if (waitForDeployment) {
325+
markAccountDeploying(accountContract);
326+
}
325327
}
326328

327329
const partialOp: UserOperationV07 = {
@@ -481,7 +483,9 @@ async function populateUserOp_v0_6(args: {
481483
accountSalt: overrides?.accountSalt,
482484
createAccountOverride: overrides?.createAccount,
483485
});
484-
markAccountDeploying(accountContract);
486+
if (waitForDeployment) {
487+
markAccountDeploying(accountContract);
488+
}
485489
}
486490

487491
const partialOp: UserOperationV06 = {

packages/thirdweb/src/wallets/smart/smart-wallet-integration-v07.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ const contract = getContract({
5252
describe.runIf(process.env.TW_SECRET_KEY).sequential(
5353
"SmartWallet 0.7 core tests",
5454
{
55+
retry: 0,
5556
timeout: 240_000,
5657
},
5758
() => {

packages/thirdweb/src/wallets/smart/smart-wallet-integration.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ const factoryAddress = "0x564cf6453a1b0FF8DB603E92EA4BbD410dea45F3"; // pre 712
5252
describe.runIf(process.env.TW_SECRET_KEY).sequential(
5353
"SmartWallet core tests",
5454
{
55+
retry: 0,
5556
timeout: 240_000,
5657
},
5758
() => {

0 commit comments

Comments
 (0)