0xtrails 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/analytics.d.ts +8 -3
- package/dist/analytics.d.ts.map +1 -1
- package/dist/{ccip-DhEkQ6QC.js → ccip-Dw5AN7oU.js} +1 -1
- package/dist/cctp.d.ts +0 -149
- package/dist/cctp.d.ts.map +1 -1
- package/dist/chains.d.ts +28 -3
- package/dist/chains.d.ts.map +1 -1
- package/dist/config.d.ts +11 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/constants.d.ts +1 -1
- package/dist/constants.d.ts.map +1 -1
- package/dist/contractUtils.d.ts.map +1 -1
- package/dist/estimate.d.ts.map +1 -1
- package/dist/fees.d.ts.map +1 -1
- package/dist/gasless.d.ts +12 -0
- package/dist/gasless.d.ts.map +1 -1
- package/dist/{index-MhD2DA7_.js → index-BtVUTbEZ.js} +30984 -38945
- package/dist/index.d.ts +7 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +108 -107
- package/dist/indexerClient.d.ts +2 -2
- package/dist/intents.d.ts +0 -17
- package/dist/intents.d.ts.map +1 -1
- package/dist/mutations.d.ts.map +1 -1
- package/dist/paymasterSend.d.ts.map +1 -1
- package/dist/prepareSend.d.ts +1 -1
- package/dist/prepareSend.d.ts.map +1 -1
- package/dist/sendUserOp.d.ts +0 -18
- package/dist/sendUserOp.d.ts.map +1 -1
- package/dist/tokenBalances.d.ts.map +1 -1
- package/dist/tokens.d.ts +10 -8
- package/dist/tokens.d.ts.map +1 -1
- package/dist/transactionIntent/deposits/depositOrchestrator.d.ts +4 -5
- package/dist/transactionIntent/deposits/depositOrchestrator.d.ts.map +1 -1
- package/dist/transactionIntent/deposits/gaslessDeposit.d.ts +4 -5
- package/dist/transactionIntent/deposits/gaslessDeposit.d.ts.map +1 -1
- package/dist/transactionIntent/deposits/standardDeposit.d.ts +2 -2
- package/dist/transactionIntent/deposits/standardDeposit.d.ts.map +1 -1
- package/dist/transactionIntent/execution/transactionState.d.ts +2 -2
- package/dist/transactionIntent/execution/transactionState.d.ts.map +1 -1
- package/dist/transactionIntent/handlers/crossChain.d.ts +4 -4
- package/dist/transactionIntent/handlers/crossChain.d.ts.map +1 -1
- package/dist/transactionIntent/handlers/index.d.ts +0 -1
- package/dist/transactionIntent/handlers/index.d.ts.map +1 -1
- package/dist/transactionIntent/handlers/sameChainSameToken.d.ts +4 -34
- package/dist/transactionIntent/handlers/sameChainSameToken.d.ts.map +1 -1
- package/dist/transactionIntent/quote/normalizeQuote.d.ts.map +1 -1
- package/dist/transactionIntent/quote/quoteHelpers.d.ts +2 -1
- package/dist/transactionIntent/quote/quoteHelpers.d.ts.map +1 -1
- package/dist/transactionIntent/types.d.ts +6 -19
- package/dist/transactionIntent/types.d.ts.map +1 -1
- package/dist/transactionIntent/utils/index.d.ts +0 -1
- package/dist/transactionIntent/utils/index.d.ts.map +1 -1
- package/dist/transactions.d.ts +2 -20
- package/dist/transactions.d.ts.map +1 -1
- package/dist/utils.d.ts +8 -2
- package/dist/utils.d.ts.map +1 -1
- package/dist/walletUtils.d.ts +21 -0
- package/dist/walletUtils.d.ts.map +1 -0
- package/dist/wallets.d.ts +33 -240
- package/dist/wallets.d.ts.map +1 -1
- package/dist/widget/components/AccountIntentTransactionHistory.d.ts.map +1 -1
- package/dist/widget/components/ClassicSwap.d.ts.map +1 -1
- package/dist/widget/components/FeeOption.d.ts +8 -13
- package/dist/widget/components/FeeOption.d.ts.map +1 -1
- package/dist/widget/components/FeeOptions.d.ts +11 -5
- package/dist/widget/components/FeeOptions.d.ts.map +1 -1
- package/dist/widget/components/NativeGasOption.d.ts.map +1 -1
- package/dist/widget/components/Pay.d.ts.map +1 -1
- package/dist/widget/components/PoolDeposit.d.ts.map +1 -1
- package/dist/widget/components/QRCodeDeposit.d.ts +5 -0
- package/dist/widget/components/QRCodeDeposit.d.ts.map +1 -1
- package/dist/widget/components/QRCodeWalletSelect.d.ts +13 -0
- package/dist/widget/components/QRCodeWalletSelect.d.ts.map +1 -0
- package/dist/widget/components/QrCode.d.ts.map +1 -1
- package/dist/widget/components/QuoteDetails.d.ts.map +1 -1
- package/dist/widget/components/Receipt.d.ts.map +1 -1
- package/dist/widget/components/ScreenHeader.d.ts +1 -1
- package/dist/widget/components/ScreenHeader.d.ts.map +1 -1
- package/dist/widget/components/Toast.d.ts.map +1 -1
- package/dist/widget/components/TokenImage.d.ts.map +1 -1
- package/dist/widget/css/compiled.css +1 -1
- package/dist/widget/hooks/useCheckout.d.ts +15 -1
- package/dist/widget/hooks/useCheckout.d.ts.map +1 -1
- package/dist/widget/hooks/useCurrentScreen.d.ts +1 -1
- package/dist/widget/hooks/useCurrentScreen.d.ts.map +1 -1
- package/dist/widget/hooks/useDebugScreens.d.ts +1 -1
- package/dist/widget/hooks/useDebugScreens.d.ts.map +1 -1
- package/dist/widget/hooks/useIntentTransactionHistory.d.ts.map +1 -1
- package/dist/widget/hooks/useIsConnectedWalletSmartContract.d.ts +7 -0
- package/dist/widget/hooks/useIsConnectedWalletSmartContract.d.ts.map +1 -0
- package/dist/widget/hooks/useIsSequenceWallet.d.ts +6 -0
- package/dist/widget/hooks/useIsSequenceWallet.d.ts.map +1 -0
- package/dist/widget/hooks/useQuote.d.ts +5 -8
- package/dist/widget/hooks/useQuote.d.ts.map +1 -1
- package/dist/widget/hooks/useRecentTokens.d.ts.map +1 -1
- package/dist/widget/hooks/useSelectedFeeOption.d.ts +30 -0
- package/dist/widget/hooks/useSelectedFeeOption.d.ts.map +1 -0
- package/dist/widget/hooks/useSendForm.d.ts +6 -15
- package/dist/widget/hooks/useSendForm.d.ts.map +1 -1
- package/dist/widget/hooks/useTokenList.d.ts.map +1 -1
- package/dist/widget/index.js +1 -1
- package/dist/widget/providers/TrailsProvider.d.ts +23 -12
- package/dist/widget/providers/TrailsProvider.d.ts.map +1 -1
- package/dist/widget/widget.d.ts +11 -0
- package/dist/widget/widget.d.ts.map +1 -1
- package/package.json +8 -8
- package/src/analytics.ts +53 -21
- package/src/cctp.ts +0 -1016
- package/src/chains.ts +93 -39
- package/src/config.ts +24 -6
- package/src/constants.ts +1 -4
- package/src/contractUtils.ts +6 -6
- package/src/estimate.ts +3 -6
- package/src/fees.ts +5 -10
- package/src/gasless.ts +45 -0
- package/src/index.ts +7 -6
- package/src/indexerClient.ts +2 -2
- package/src/intents.ts +52 -206
- package/src/mutations.ts +3 -2
- package/src/paymasterSend.ts +2 -5
- package/src/prepareSend.ts +9 -12
- package/src/sendUserOp.ts +3 -64
- package/src/tokenBalances.ts +2 -1
- package/src/tokens.ts +62 -133
- package/src/trailsClient.ts +1 -1
- package/src/transactionIntent/deposits/depositOrchestrator.ts +14 -15
- package/src/transactionIntent/deposits/gaslessDeposit.ts +70 -100
- package/src/transactionIntent/deposits/standardDeposit.ts +22 -28
- package/src/transactionIntent/execution/transactionState.ts +2 -2
- package/src/transactionIntent/handlers/crossChain.ts +165 -385
- package/src/transactionIntent/handlers/index.ts +0 -1
- package/src/transactionIntent/handlers/sameChainSameToken.ts +228 -94
- package/src/transactionIntent/quote/normalizeQuote.ts +4 -6
- package/src/transactionIntent/quote/quoteHelpers.ts +35 -3
- package/src/transactionIntent/types.ts +6 -27
- package/src/transactionIntent/utils/index.ts +0 -1
- package/src/transactions.ts +6 -203
- package/src/umd.tsx +1 -3
- package/src/utils.ts +28 -8
- package/src/walletUtils.ts +42 -0
- package/src/wallets.ts +361 -203
- package/src/widget/compiled.css +1 -1
- package/src/widget/components/AccountIntentTransactionHistory.tsx +73 -4
- package/src/widget/components/AccountSettings.tsx +17 -17
- package/src/widget/components/ChainList.tsx +3 -3
- package/src/widget/components/ClassicSwap.tsx +19 -10
- package/src/widget/components/ConfigDisplay.tsx +1 -1
- package/src/widget/components/FeeOption.tsx +63 -20
- package/src/widget/components/FeeOptions.tsx +54 -123
- package/src/widget/components/NativeGasOption.tsx +3 -1
- package/src/widget/components/Pay.tsx +18 -11
- package/src/widget/components/PoolDeposit.tsx +23 -10
- package/src/widget/components/QRCodeDeposit.tsx +50 -30
- package/src/widget/components/QRCodeWalletSelect.tsx +77 -0
- package/src/widget/components/QrCode.tsx +188 -233
- package/src/widget/components/QuoteDetails.tsx +48 -2
- package/src/widget/components/Receipt.tsx +5 -2
- package/src/widget/components/ScreenHeader.tsx +10 -8
- package/src/widget/components/Toast.tsx +10 -0
- package/src/widget/components/TokenImage.tsx +56 -13
- package/src/widget/hooks/useCheckout.ts +71 -0
- package/src/widget/hooks/useCurrentScreen.tsx +1 -0
- package/src/widget/hooks/useDebugScreens.ts +5 -0
- package/src/widget/hooks/useIntentTransactionHistory.ts +788 -418
- package/src/widget/hooks/useIsConnectedWalletSmartContract.ts +43 -0
- package/src/widget/hooks/useIsSequenceWallet.ts +17 -0
- package/src/widget/hooks/useQuote.ts +16 -17
- package/src/widget/hooks/useRecentTokens.ts +2 -1
- package/src/widget/hooks/useSelectedFeeOption.tsx +257 -0
- package/src/widget/hooks/useSendForm.ts +172 -47
- package/src/widget/hooks/useTokenList.ts +15 -2
- package/src/widget/providers/TrailsProvider.tsx +53 -25
- package/src/widget/widget.tsx +119 -48
- package/dist/cctpqueue.d.ts +0 -18
- package/dist/cctpqueue.d.ts.map +0 -1
- package/dist/preconditions.d.ts +0 -12
- package/dist/preconditions.d.ts.map +0 -1
- package/dist/transactionIntent/handlers/sameChainDifferentToken.d.ts +0 -62
- package/dist/transactionIntent/handlers/sameChainDifferentToken.d.ts.map +0 -1
- package/dist/transactionIntent/utils/lifiHelpers.d.ts +0 -10
- package/dist/transactionIntent/utils/lifiHelpers.d.ts.map +0 -1
- package/dist/widget/hooks/useSelectedFeeToken.d.ts +0 -33
- package/dist/widget/hooks/useSelectedFeeToken.d.ts.map +0 -1
- package/src/cctpqueue.ts +0 -69
- package/src/preconditions.ts +0 -47
- package/src/transactionIntent/handlers/sameChainDifferentToken.ts +0 -323
- package/src/transactionIntent/utils/lifiHelpers.ts +0 -68
- package/src/widget/hooks/useSelectedFeeToken.tsx +0 -288
package/src/cctp.ts
CHANGED
|
@@ -1,12 +1,3 @@
|
|
|
1
|
-
import type { Chain, PublicClient, WalletClient } from "viem"
|
|
2
|
-
import {
|
|
3
|
-
createPublicClient,
|
|
4
|
-
encodeFunctionData,
|
|
5
|
-
erc20Abi,
|
|
6
|
-
getAddress,
|
|
7
|
-
http,
|
|
8
|
-
maxUint256,
|
|
9
|
-
} from "viem"
|
|
10
1
|
import {
|
|
11
2
|
mainnet,
|
|
12
3
|
sepolia,
|
|
@@ -27,30 +18,6 @@ import {
|
|
|
27
18
|
worldchain,
|
|
28
19
|
worldchainSepolia,
|
|
29
20
|
} from "viem/chains"
|
|
30
|
-
import { attemptSwitchChain } from "./chainSwitch.js"
|
|
31
|
-
import { getIsTestnetChainId } from "./chains.js"
|
|
32
|
-
import { logger } from "./logger.js"
|
|
33
|
-
|
|
34
|
-
const domains: Record<number, number> = {
|
|
35
|
-
[mainnet.id]: 0,
|
|
36
|
-
[sepolia.id]: 0,
|
|
37
|
-
[avalanche.id]: 1,
|
|
38
|
-
[avalancheFuji.id]: 1,
|
|
39
|
-
[optimism.id]: 2,
|
|
40
|
-
[optimismSepolia.id]: 2,
|
|
41
|
-
[arbitrum.id]: 3,
|
|
42
|
-
[arbitrumSepolia.id]: 3,
|
|
43
|
-
[base.id]: 6,
|
|
44
|
-
[baseSepolia.id]: 6,
|
|
45
|
-
[polygon.id]: 7,
|
|
46
|
-
[polygonAmoy.id]: 7,
|
|
47
|
-
[unichain.id]: 10,
|
|
48
|
-
[unichainSepolia.id]: 10,
|
|
49
|
-
[linea.id]: 11,
|
|
50
|
-
[lineaSepolia.id]: 11,
|
|
51
|
-
[worldchain.id]: 14,
|
|
52
|
-
[worldchainSepolia.id]: 14,
|
|
53
|
-
}
|
|
54
21
|
|
|
55
22
|
const tokenAddresses: Record<number, string> = {
|
|
56
23
|
// Mainnet USDC addresses from Circle CCTP documentation
|
|
@@ -78,993 +45,10 @@ const tokenAddresses: Record<number, string> = {
|
|
|
78
45
|
[worldchain.id]: "0x79A02482A880bCe3F13E09da970dC34dB4cD24D1", // World Chain
|
|
79
46
|
}
|
|
80
47
|
|
|
81
|
-
const tokenMessengers: Record<number, string> = {
|
|
82
|
-
[mainnet.id]: "0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d",
|
|
83
|
-
[sepolia.id]: "0x8FE6B999Dc680CcFDD5Bf7EB0974218be2542DAA",
|
|
84
|
-
[avalanche.id]: "0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d",
|
|
85
|
-
[avalancheFuji.id]: "0x8FE6B999Dc680CcFDD5Bf7EB0974218be2542DAA",
|
|
86
|
-
[optimism.id]: "0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d",
|
|
87
|
-
[optimismSepolia.id]: "0x8FE6B999Dc680CcFDD5Bf7EB0974218be2542DAA",
|
|
88
|
-
[arbitrum.id]: "0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d",
|
|
89
|
-
[arbitrumSepolia.id]: "0x8FE6B999Dc680CcFDD5Bf7EB0974218be2542DAA",
|
|
90
|
-
[base.id]: "0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d",
|
|
91
|
-
[baseSepolia.id]: "0x8FE6B999Dc680CcFDD5Bf7EB0974218be2542DAA",
|
|
92
|
-
[polygon.id]: "0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d",
|
|
93
|
-
[polygonAmoy.id]: "0x8FE6B999Dc680CcFDD5Bf7EB0974218be2542DAA",
|
|
94
|
-
[unichain.id]: "0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d",
|
|
95
|
-
[unichainSepolia.id]: "0x8FE6B999Dc680CcFDD5Bf7EB0974218be2542DAA",
|
|
96
|
-
[linea.id]: "0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d",
|
|
97
|
-
[lineaSepolia.id]: "0x8FE6B999Dc680CcFDD5Bf7EB0974218be2542DAA",
|
|
98
|
-
[worldchain.id]: "0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d",
|
|
99
|
-
[worldchainSepolia.id]: "0x8FE6B999Dc680CcFDD5Bf7EB0974218be2542DAA",
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
const messageTransmitters: Record<number, string> = {
|
|
103
|
-
[mainnet.id]: "0x81D40F21F12A8F0E3252Bccb954D722d4c464B64",
|
|
104
|
-
[sepolia.id]: "0xE737e5cEBEEBa77EFE34D4aa090756590b1CE275",
|
|
105
|
-
[avalanche.id]: "0x81D40F21F12A8F0E3252Bccb954D722d4c464B64",
|
|
106
|
-
[avalancheFuji.id]: "0xE737e5cEBEEBa77EFE34D4aa090756590b1CE275",
|
|
107
|
-
[optimism.id]: "0x81D40F21F12A8F0E3252Bccb954D722d4c464B64",
|
|
108
|
-
[optimismSepolia.id]: "0xE737e5cEBEEBa77EFE34D4aa090756590b1CE275",
|
|
109
|
-
[arbitrum.id]: "0x81D40F21F12A8F0E3252Bccb954D722d4c464B64",
|
|
110
|
-
[arbitrumSepolia.id]: "0xE737e5cEBEEBa77EFE34D4aa090756590b1CE275",
|
|
111
|
-
[base.id]: "0x81D40F21F12A8F0E3252Bccb954D722d4c464B64",
|
|
112
|
-
[baseSepolia.id]: "0xE737e5cEBEEBa77EFE34D4aa090756590b1CE275",
|
|
113
|
-
[polygon.id]: "0x81D40F21F12A8F0E3252Bccb954D722d4c464B64",
|
|
114
|
-
[polygonAmoy.id]: "0xE737e5cEBEEBa77EFE34D4aa090756590b1CE275",
|
|
115
|
-
[unichain.id]: "0x81D40F21F12A8F0E3252Bccb954D722d4c464B64",
|
|
116
|
-
[unichainSepolia.id]: "0xE737e5cEBEEBa77EFE34D4aa090756590b1CE275",
|
|
117
|
-
[linea.id]: "0x81D40F21F12A8F0E3252Bccb954D722d4c464B64",
|
|
118
|
-
[lineaSepolia.id]: "0xE737e5cEBEEBa77EFE34D4aa090756590b1CE275",
|
|
119
|
-
[worldchain.id]: "0x81D40F21F12A8F0E3252Bccb954D722d4c464B64",
|
|
120
|
-
[worldchainSepolia.id]: "0xE737e5cEBEEBa77EFE34D4aa090756590b1CE275",
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
const customCctpRelayerAddress: Record<number, string> = {
|
|
124
|
-
//[chains.arbitrumSepolia.id]: "0x58FEFe8A057E736CD361272BA2283DAfD8646198",
|
|
125
|
-
[arbitrumSepolia.id]: "0x05F3AcC7a7BB0e888Bb1bDE014bD61AfAfaC6943",
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
export type Attestation = {
|
|
129
|
-
attestation: `0x${string}`
|
|
130
|
-
message: `0x${string}`
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
export function getDomain(chainId: number): number | null {
|
|
134
|
-
return domains[chainId] ?? null
|
|
135
|
-
}
|
|
136
|
-
|
|
137
48
|
export function getUSDCTokenAddress(chainId: number): string | null {
|
|
138
49
|
return tokenAddresses[chainId] ?? null
|
|
139
50
|
}
|
|
140
51
|
|
|
141
|
-
export function getTokenMessenger(chainId: number): string | null {
|
|
142
|
-
return tokenMessengers[chainId] ?? null
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
export function getMessageTransmitter(chainId: number): string | null {
|
|
146
|
-
return messageTransmitters[chainId] ?? null
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
export async function cctpTransfer({
|
|
150
|
-
walletClient,
|
|
151
|
-
originChain,
|
|
152
|
-
destinationChain,
|
|
153
|
-
amount,
|
|
154
|
-
}: {
|
|
155
|
-
walletClient: WalletClient
|
|
156
|
-
originChain: Chain
|
|
157
|
-
destinationChain: Chain
|
|
158
|
-
amount: bigint
|
|
159
|
-
}): Promise<{
|
|
160
|
-
waitForAttestation: () => Promise<Attestation>
|
|
161
|
-
txHash: `0x${string}`
|
|
162
|
-
}> {
|
|
163
|
-
const originToken = getUSDCTokenAddress(originChain.id)
|
|
164
|
-
const originDomain = getDomain(originChain.id)
|
|
165
|
-
const destinationDomain = getDomain(destinationChain.id)
|
|
166
|
-
const originTokenMessenger = getTokenMessenger(originChain.id)
|
|
167
|
-
const destinationAddress = walletClient.account?.address
|
|
168
|
-
|
|
169
|
-
if (
|
|
170
|
-
!originToken ||
|
|
171
|
-
originDomain === null ||
|
|
172
|
-
!originTokenMessenger ||
|
|
173
|
-
destinationDomain === null ||
|
|
174
|
-
!destinationAddress
|
|
175
|
-
) {
|
|
176
|
-
logger.console.error(
|
|
177
|
-
"[trails-sdk] cctpTransfer: Invalid origin chain config",
|
|
178
|
-
{
|
|
179
|
-
originToken,
|
|
180
|
-
originDomain,
|
|
181
|
-
originTokenMessenger,
|
|
182
|
-
destinationDomain,
|
|
183
|
-
destinationAddress,
|
|
184
|
-
},
|
|
185
|
-
)
|
|
186
|
-
throw new Error("Invalid origin chain config")
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
const originClient = createPublicClient({
|
|
190
|
-
chain: originChain,
|
|
191
|
-
transport: http(),
|
|
192
|
-
})
|
|
193
|
-
|
|
194
|
-
await attemptSwitchChain({
|
|
195
|
-
walletClient,
|
|
196
|
-
desiredChainId: originChain.id,
|
|
197
|
-
})
|
|
198
|
-
|
|
199
|
-
const account = walletClient.account?.address
|
|
200
|
-
if (!account) {
|
|
201
|
-
throw new Error("No account found")
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
const needsApproval = await getNeedsApproval({
|
|
205
|
-
publicClient: originClient,
|
|
206
|
-
token: originToken,
|
|
207
|
-
account,
|
|
208
|
-
spender: originTokenMessenger,
|
|
209
|
-
amount: maxUint256,
|
|
210
|
-
})
|
|
211
|
-
|
|
212
|
-
if (needsApproval) {
|
|
213
|
-
const txHash = await approveERC20({
|
|
214
|
-
walletClient,
|
|
215
|
-
tokenAddress: originToken,
|
|
216
|
-
spender: originTokenMessenger,
|
|
217
|
-
amount: maxUint256,
|
|
218
|
-
chain: originChain,
|
|
219
|
-
})
|
|
220
|
-
|
|
221
|
-
logger.console.log("waiting for approve", txHash)
|
|
222
|
-
await originClient.waitForTransactionReceipt({
|
|
223
|
-
hash: txHash,
|
|
224
|
-
})
|
|
225
|
-
logger.console.log("approve done")
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
const maxFee = getMaxFee()
|
|
229
|
-
|
|
230
|
-
const txHash = await burnUSDC({
|
|
231
|
-
walletClient,
|
|
232
|
-
tokenMessenger: originTokenMessenger,
|
|
233
|
-
destinationDomain,
|
|
234
|
-
destinationAddress,
|
|
235
|
-
amount,
|
|
236
|
-
burnToken: originToken,
|
|
237
|
-
maxFee,
|
|
238
|
-
chain: originChain,
|
|
239
|
-
})
|
|
240
|
-
|
|
241
|
-
return {
|
|
242
|
-
waitForAttestation: async () => {
|
|
243
|
-
await originClient.waitForTransactionReceipt({
|
|
244
|
-
hash: txHash,
|
|
245
|
-
})
|
|
246
|
-
|
|
247
|
-
const testnet = getIsTestnetChainId(originChain.id)
|
|
248
|
-
|
|
249
|
-
const attestation = await waitForAttestation({
|
|
250
|
-
domain: originDomain,
|
|
251
|
-
transactionHash: txHash,
|
|
252
|
-
testnet,
|
|
253
|
-
})
|
|
254
|
-
|
|
255
|
-
if (!attestation) {
|
|
256
|
-
throw new Error("Failed to retrieve attestation")
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
return attestation
|
|
260
|
-
},
|
|
261
|
-
txHash: txHash,
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
export async function cctpDestinationTx({
|
|
266
|
-
relayerClient,
|
|
267
|
-
destinationChain,
|
|
268
|
-
attestation,
|
|
269
|
-
}: {
|
|
270
|
-
relayerClient: WalletClient
|
|
271
|
-
destinationChain: Chain
|
|
272
|
-
attestation: Attestation
|
|
273
|
-
}): Promise<`0x${string}`> {
|
|
274
|
-
const destinationTokenMessenger = getMessageTransmitter(destinationChain.id)
|
|
275
|
-
|
|
276
|
-
if (!destinationTokenMessenger) {
|
|
277
|
-
throw new Error("Invalid destination chain")
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
await attemptSwitchChain({
|
|
281
|
-
walletClient: relayerClient,
|
|
282
|
-
desiredChainId: destinationChain.id,
|
|
283
|
-
})
|
|
284
|
-
|
|
285
|
-
const txHash = await mintUSDC({
|
|
286
|
-
walletClient: relayerClient,
|
|
287
|
-
tokenMessenger: destinationTokenMessenger,
|
|
288
|
-
attestation,
|
|
289
|
-
chain: destinationChain,
|
|
290
|
-
})
|
|
291
|
-
|
|
292
|
-
logger.console.log("[trails-sdk] minted USDC")
|
|
293
|
-
return txHash
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
export function getMaxFee(): bigint {
|
|
297
|
-
return 500n // Set fast transfer max fee in 10^6 subunits (0.0005 USDC; change as needed)
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
export async function getNeedsApproval({
|
|
301
|
-
publicClient,
|
|
302
|
-
token,
|
|
303
|
-
account,
|
|
304
|
-
spender,
|
|
305
|
-
amount,
|
|
306
|
-
}: {
|
|
307
|
-
publicClient: PublicClient
|
|
308
|
-
token: string
|
|
309
|
-
account: string
|
|
310
|
-
spender: string
|
|
311
|
-
amount: bigint
|
|
312
|
-
}): Promise<boolean> {
|
|
313
|
-
if (account?.toLowerCase() === spender.toLowerCase()) {
|
|
314
|
-
return false
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
const allowance = await publicClient.readContract({
|
|
318
|
-
address: token as `0x${string}`,
|
|
319
|
-
abi: erc20Abi,
|
|
320
|
-
functionName: "allowance",
|
|
321
|
-
args: [getAddress(account), getAddress(spender)],
|
|
322
|
-
})
|
|
323
|
-
|
|
324
|
-
return allowance < amount
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
export async function approveERC20({
|
|
328
|
-
walletClient,
|
|
329
|
-
tokenAddress,
|
|
330
|
-
spender,
|
|
331
|
-
amount,
|
|
332
|
-
chain,
|
|
333
|
-
}: {
|
|
334
|
-
walletClient: WalletClient
|
|
335
|
-
tokenAddress: string
|
|
336
|
-
spender: string
|
|
337
|
-
amount: bigint
|
|
338
|
-
chain: Chain
|
|
339
|
-
}): Promise<`0x${string}`> {
|
|
340
|
-
const approvalData = await getApproveERC20Data({
|
|
341
|
-
tokenAddress,
|
|
342
|
-
spender,
|
|
343
|
-
amount,
|
|
344
|
-
})
|
|
345
|
-
|
|
346
|
-
logger.console.log("[trails-sdk] approving ERC20 transfer", approvalData)
|
|
347
|
-
|
|
348
|
-
await attemptSwitchChain({
|
|
349
|
-
walletClient,
|
|
350
|
-
desiredChainId: chain.id,
|
|
351
|
-
})
|
|
352
|
-
|
|
353
|
-
const account = walletClient.account?.address
|
|
354
|
-
if (!account) {
|
|
355
|
-
throw new Error("No account found")
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
const txHash = await walletClient.sendTransaction({
|
|
359
|
-
...approvalData,
|
|
360
|
-
account: account as `0x${string}`,
|
|
361
|
-
chain,
|
|
362
|
-
})
|
|
363
|
-
|
|
364
|
-
if (!txHash) {
|
|
365
|
-
throw new Error(
|
|
366
|
-
"No transaction hash returned from walletClient.sendTransaction",
|
|
367
|
-
)
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
return txHash
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
async function getApproveERC20Data({
|
|
374
|
-
tokenAddress,
|
|
375
|
-
spender,
|
|
376
|
-
amount,
|
|
377
|
-
}: {
|
|
378
|
-
tokenAddress: string
|
|
379
|
-
spender: string
|
|
380
|
-
amount: bigint
|
|
381
|
-
}): Promise<{ to: `0x${string}`; data: `0x${string}`; value: bigint }> {
|
|
382
|
-
logger.console.log("[trails-sdk] get approve ERC20 transfer data", {
|
|
383
|
-
tokenAddress,
|
|
384
|
-
spender,
|
|
385
|
-
amount,
|
|
386
|
-
})
|
|
387
|
-
return {
|
|
388
|
-
to: tokenAddress as `0x${string}`,
|
|
389
|
-
value: BigInt(0),
|
|
390
|
-
data: encodeFunctionData({
|
|
391
|
-
abi: [
|
|
392
|
-
{
|
|
393
|
-
type: "function",
|
|
394
|
-
name: "approve",
|
|
395
|
-
stateMutability: "nonpayable",
|
|
396
|
-
inputs: [
|
|
397
|
-
{ name: "spender", type: "address" },
|
|
398
|
-
{ name: "amount", type: "uint256" },
|
|
399
|
-
],
|
|
400
|
-
outputs: [{ name: "", type: "bool" }],
|
|
401
|
-
},
|
|
402
|
-
],
|
|
403
|
-
functionName: "approve",
|
|
404
|
-
args: [spender as `0x${string}`, amount],
|
|
405
|
-
}),
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
export async function burnUSDC({
|
|
410
|
-
walletClient,
|
|
411
|
-
tokenMessenger,
|
|
412
|
-
destinationDomain,
|
|
413
|
-
destinationAddress,
|
|
414
|
-
amount,
|
|
415
|
-
burnToken,
|
|
416
|
-
maxFee,
|
|
417
|
-
chain,
|
|
418
|
-
}: {
|
|
419
|
-
walletClient: WalletClient
|
|
420
|
-
tokenMessenger: string
|
|
421
|
-
destinationDomain: number
|
|
422
|
-
destinationAddress: string
|
|
423
|
-
amount: bigint
|
|
424
|
-
burnToken: string
|
|
425
|
-
maxFee: bigint
|
|
426
|
-
chain: Chain
|
|
427
|
-
}): Promise<`0x${string}`> {
|
|
428
|
-
const burnData = await getBurnUSDCData({
|
|
429
|
-
tokenMessenger,
|
|
430
|
-
destinationDomain,
|
|
431
|
-
destinationAddress,
|
|
432
|
-
amount,
|
|
433
|
-
burnToken,
|
|
434
|
-
maxFee: maxFee,
|
|
435
|
-
})
|
|
436
|
-
|
|
437
|
-
await attemptSwitchChain({
|
|
438
|
-
walletClient,
|
|
439
|
-
desiredChainId: chain.id,
|
|
440
|
-
})
|
|
441
|
-
|
|
442
|
-
const account = walletClient.account?.address
|
|
443
|
-
if (!account) {
|
|
444
|
-
throw new Error("No account found")
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
return walletClient.sendTransaction({
|
|
448
|
-
...burnData,
|
|
449
|
-
account: account as `0x${string}`,
|
|
450
|
-
chain,
|
|
451
|
-
})
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
export async function getBurnUSDCData({
|
|
455
|
-
tokenMessenger,
|
|
456
|
-
destinationDomain,
|
|
457
|
-
destinationAddress,
|
|
458
|
-
amount,
|
|
459
|
-
burnToken,
|
|
460
|
-
maxFee,
|
|
461
|
-
}: {
|
|
462
|
-
tokenMessenger: string
|
|
463
|
-
destinationDomain: number
|
|
464
|
-
destinationAddress: string
|
|
465
|
-
amount: bigint
|
|
466
|
-
burnToken: string
|
|
467
|
-
maxFee: bigint
|
|
468
|
-
}): Promise<{ to: `0x${string}`; data: `0x${string}`; value: bigint }> {
|
|
469
|
-
logger.console.log("[trails-sdk] get burn USDC data", {
|
|
470
|
-
tokenMessenger,
|
|
471
|
-
destinationDomain,
|
|
472
|
-
destinationAddress,
|
|
473
|
-
amount,
|
|
474
|
-
burnToken,
|
|
475
|
-
maxFee,
|
|
476
|
-
})
|
|
477
|
-
// Bytes32 Formatted Parameters
|
|
478
|
-
const DESTINATION_ADDRESS_BYTES32 = `0x000000000000000000000000${destinationAddress.slice(2)}` // Destination address in bytes32 format
|
|
479
|
-
const DESTINATION_CALLER_BYTES32 =
|
|
480
|
-
"0x0000000000000000000000000000000000000000000000000000000000000000" // Empty bytes32 allows any address to call MessageTransmitterV2.receiveMessage()
|
|
481
|
-
|
|
482
|
-
return {
|
|
483
|
-
to: tokenMessenger as `0x${string}`,
|
|
484
|
-
value: BigInt(0),
|
|
485
|
-
data: encodeFunctionData({
|
|
486
|
-
abi: [
|
|
487
|
-
{
|
|
488
|
-
type: "function",
|
|
489
|
-
name: "depositForBurn",
|
|
490
|
-
stateMutability: "nonpayable",
|
|
491
|
-
inputs: [
|
|
492
|
-
{ name: "amount", type: "uint256" },
|
|
493
|
-
{ name: "destinationDomain", type: "uint32" },
|
|
494
|
-
{ name: "mintRecipient", type: "bytes32" },
|
|
495
|
-
{ name: "burnToken", type: "address" },
|
|
496
|
-
{ name: "destinationCaller", type: "bytes32" },
|
|
497
|
-
{ name: "maxFee", type: "uint256" },
|
|
498
|
-
{ name: "minFinalityThreshold", type: "uint32" },
|
|
499
|
-
],
|
|
500
|
-
outputs: [],
|
|
501
|
-
},
|
|
502
|
-
],
|
|
503
|
-
functionName: "depositForBurn",
|
|
504
|
-
args: [
|
|
505
|
-
amount,
|
|
506
|
-
destinationDomain,
|
|
507
|
-
DESTINATION_ADDRESS_BYTES32 as `0x${string}`,
|
|
508
|
-
burnToken as `0x${string}`,
|
|
509
|
-
DESTINATION_CALLER_BYTES32 as `0x${string}`,
|
|
510
|
-
maxFee,
|
|
511
|
-
1000, // minFinalityThreshold (1000 or less for Fast Transfer)
|
|
512
|
-
],
|
|
513
|
-
}),
|
|
514
|
-
}
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
export async function retrieveAttestation({
|
|
518
|
-
domain,
|
|
519
|
-
transactionHash,
|
|
520
|
-
testnet,
|
|
521
|
-
}: {
|
|
522
|
-
domain: number
|
|
523
|
-
transactionHash: `0x${string}`
|
|
524
|
-
testnet: boolean
|
|
525
|
-
}): Promise<Attestation | null> {
|
|
526
|
-
logger.console.log("[trails-sdk] retrieving attestation", {
|
|
527
|
-
domain,
|
|
528
|
-
transactionHash,
|
|
529
|
-
})
|
|
530
|
-
const url = `https://iris-api${testnet ? "-sandbox" : ""}.circle.com/v2/messages/${domain}?transactionHash=${transactionHash}`
|
|
531
|
-
while (true) {
|
|
532
|
-
try {
|
|
533
|
-
const response = await fetch(url)
|
|
534
|
-
const data = await response.json()
|
|
535
|
-
if (response.status === 404) {
|
|
536
|
-
logger.console.log("[trails-sdk] waiting for attestation...")
|
|
537
|
-
}
|
|
538
|
-
if (data?.messages?.[0]?.status === "complete") {
|
|
539
|
-
logger.console.log("[trails-sdk] attestation retrieved successfully!")
|
|
540
|
-
return data.messages[0]
|
|
541
|
-
}
|
|
542
|
-
logger.console.log("[trails-sdk] waiting for attestation...")
|
|
543
|
-
await new Promise((resolve) => setTimeout(resolve, 5000))
|
|
544
|
-
} catch (error: unknown) {
|
|
545
|
-
logger.console.error(
|
|
546
|
-
"[trails-sdk] error fetching attestation:",
|
|
547
|
-
error instanceof Error ? error.message : String(error),
|
|
548
|
-
)
|
|
549
|
-
await new Promise((resolve) => setTimeout(resolve, 5000))
|
|
550
|
-
}
|
|
551
|
-
}
|
|
552
|
-
}
|
|
553
|
-
|
|
554
|
-
export async function mintUSDC({
|
|
555
|
-
walletClient,
|
|
556
|
-
tokenMessenger,
|
|
557
|
-
attestation,
|
|
558
|
-
chain,
|
|
559
|
-
}: {
|
|
560
|
-
walletClient: WalletClient
|
|
561
|
-
tokenMessenger: string
|
|
562
|
-
attestation: Attestation
|
|
563
|
-
chain: Chain
|
|
564
|
-
}): Promise<`0x${string}`> {
|
|
565
|
-
const mintData = await getMintUSDCData({
|
|
566
|
-
tokenMessenger,
|
|
567
|
-
attestation,
|
|
568
|
-
})
|
|
569
|
-
|
|
570
|
-
await attemptSwitchChain({
|
|
571
|
-
walletClient,
|
|
572
|
-
desiredChainId: chain.id,
|
|
573
|
-
})
|
|
574
|
-
|
|
575
|
-
const account = walletClient.account?.address
|
|
576
|
-
if (!account) {
|
|
577
|
-
throw new Error("No account found")
|
|
578
|
-
}
|
|
579
|
-
|
|
580
|
-
return walletClient.sendTransaction({
|
|
581
|
-
...mintData,
|
|
582
|
-
account: account as `0x${string}`,
|
|
583
|
-
chain,
|
|
584
|
-
})
|
|
585
|
-
}
|
|
586
|
-
|
|
587
|
-
export async function getMintUSDCData({
|
|
588
|
-
tokenMessenger,
|
|
589
|
-
attestation,
|
|
590
|
-
}: {
|
|
591
|
-
tokenMessenger: string
|
|
592
|
-
attestation: Attestation
|
|
593
|
-
}): Promise<{ to: `0x${string}`; data: `0x${string}`; value: bigint }> {
|
|
594
|
-
logger.console.log("[trails-sdk] get mint USDC data", {
|
|
595
|
-
tokenMessenger,
|
|
596
|
-
attestation,
|
|
597
|
-
})
|
|
598
|
-
return {
|
|
599
|
-
to: tokenMessenger as `0x${string}`,
|
|
600
|
-
value: BigInt(0),
|
|
601
|
-
data: encodeFunctionData({
|
|
602
|
-
abi: [
|
|
603
|
-
{
|
|
604
|
-
type: "function",
|
|
605
|
-
name: "receiveMessage",
|
|
606
|
-
stateMutability: "nonpayable",
|
|
607
|
-
inputs: [
|
|
608
|
-
{ name: "message", type: "bytes" },
|
|
609
|
-
{ name: "attestation", type: "bytes" },
|
|
610
|
-
],
|
|
611
|
-
outputs: [],
|
|
612
|
-
},
|
|
613
|
-
],
|
|
614
|
-
functionName: "receiveMessage",
|
|
615
|
-
args: [attestation.message, attestation.attestation],
|
|
616
|
-
}),
|
|
617
|
-
}
|
|
618
|
-
}
|
|
619
|
-
|
|
620
|
-
export async function waitForAttestation({
|
|
621
|
-
domain,
|
|
622
|
-
transactionHash,
|
|
623
|
-
testnet,
|
|
624
|
-
}: {
|
|
625
|
-
domain: number
|
|
626
|
-
transactionHash: `0x${string}`
|
|
627
|
-
testnet: boolean
|
|
628
|
-
}): Promise<Attestation | null> {
|
|
629
|
-
while (true) {
|
|
630
|
-
const attestation = await retrieveAttestation({
|
|
631
|
-
domain,
|
|
632
|
-
transactionHash,
|
|
633
|
-
testnet,
|
|
634
|
-
})
|
|
635
|
-
if (attestation) {
|
|
636
|
-
return attestation
|
|
637
|
-
}
|
|
638
|
-
logger.console.log("[trails-sdk] waiting for attestation...")
|
|
639
|
-
await new Promise((resolve) => setTimeout(resolve, 1000))
|
|
640
|
-
}
|
|
641
|
-
}
|
|
642
|
-
|
|
643
52
|
export function getIsUsdcAddress(address: string, chainId: number): boolean {
|
|
644
53
|
return address?.toLowerCase() === tokenAddresses[chainId]?.toLowerCase()
|
|
645
54
|
}
|
|
646
|
-
|
|
647
|
-
export async function cctpTransferWithCustomCall({
|
|
648
|
-
walletClient,
|
|
649
|
-
originChain,
|
|
650
|
-
destinationChain,
|
|
651
|
-
amount,
|
|
652
|
-
}: {
|
|
653
|
-
walletClient: WalletClient
|
|
654
|
-
originChain: Chain
|
|
655
|
-
destinationChain: Chain
|
|
656
|
-
amount: bigint
|
|
657
|
-
}): Promise<{
|
|
658
|
-
waitForAttestation: () => Promise<Attestation>
|
|
659
|
-
txHash: `0x${string}`
|
|
660
|
-
}> {
|
|
661
|
-
const destinationContract = customCctpRelayerAddress[destinationChain.id]
|
|
662
|
-
if (!destinationContract) {
|
|
663
|
-
logger.console.error(
|
|
664
|
-
"[trails-sdk] cctpTransferWithCustomCall: No custom CCTP relayer address found for this chain",
|
|
665
|
-
{
|
|
666
|
-
originChain,
|
|
667
|
-
destinationChain,
|
|
668
|
-
},
|
|
669
|
-
)
|
|
670
|
-
throw new Error("No custom CCTP relayer address found for this chain")
|
|
671
|
-
}
|
|
672
|
-
|
|
673
|
-
const originToken = getUSDCTokenAddress(originChain.id)
|
|
674
|
-
const originDomain = getDomain(originChain.id)
|
|
675
|
-
const destinationDomain = getDomain(destinationChain.id)
|
|
676
|
-
const originTokenMessenger = getTokenMessenger(originChain.id)
|
|
677
|
-
|
|
678
|
-
if (
|
|
679
|
-
!originToken ||
|
|
680
|
-
originDomain === null ||
|
|
681
|
-
!originTokenMessenger ||
|
|
682
|
-
destinationDomain === null
|
|
683
|
-
) {
|
|
684
|
-
logger.console.error(
|
|
685
|
-
"[trails-sdk] cctpTransferWithCustomCall: Invalid origin chain config",
|
|
686
|
-
)
|
|
687
|
-
throw new Error("Invalid origin chain config")
|
|
688
|
-
}
|
|
689
|
-
|
|
690
|
-
const originClient = createPublicClient({
|
|
691
|
-
chain: originChain,
|
|
692
|
-
transport: http(),
|
|
693
|
-
})
|
|
694
|
-
|
|
695
|
-
await attemptSwitchChain({
|
|
696
|
-
walletClient,
|
|
697
|
-
desiredChainId: originChain.id,
|
|
698
|
-
})
|
|
699
|
-
|
|
700
|
-
const account = walletClient.account?.address
|
|
701
|
-
if (!account) {
|
|
702
|
-
throw new Error("No account found")
|
|
703
|
-
}
|
|
704
|
-
|
|
705
|
-
const needsApproval = await getNeedsApproval({
|
|
706
|
-
publicClient: originClient,
|
|
707
|
-
token: originToken,
|
|
708
|
-
account,
|
|
709
|
-
spender: originTokenMessenger,
|
|
710
|
-
amount: maxUint256,
|
|
711
|
-
})
|
|
712
|
-
|
|
713
|
-
if (needsApproval) {
|
|
714
|
-
const txHash = await approveERC20({
|
|
715
|
-
walletClient,
|
|
716
|
-
tokenAddress: originToken,
|
|
717
|
-
spender: originTokenMessenger,
|
|
718
|
-
amount: maxUint256,
|
|
719
|
-
chain: originChain,
|
|
720
|
-
})
|
|
721
|
-
|
|
722
|
-
logger.console.log("waiting for approve", txHash)
|
|
723
|
-
await originClient.waitForTransactionReceipt({
|
|
724
|
-
hash: txHash,
|
|
725
|
-
})
|
|
726
|
-
logger.console.log("approve done")
|
|
727
|
-
}
|
|
728
|
-
|
|
729
|
-
const maxFee = getMaxFee()
|
|
730
|
-
|
|
731
|
-
// Send USDC to your CCTPRelayer contract instead of user wallet
|
|
732
|
-
const txHash = await burnUSDCToContract({
|
|
733
|
-
walletClient,
|
|
734
|
-
tokenMessenger: originTokenMessenger,
|
|
735
|
-
destinationDomain,
|
|
736
|
-
destinationContract, // CCTPRelayer contract address
|
|
737
|
-
amount,
|
|
738
|
-
burnToken: originToken,
|
|
739
|
-
maxFee,
|
|
740
|
-
chain: originChain,
|
|
741
|
-
})
|
|
742
|
-
|
|
743
|
-
return {
|
|
744
|
-
waitForAttestation: async () => {
|
|
745
|
-
await originClient.waitForTransactionReceipt({
|
|
746
|
-
hash: txHash,
|
|
747
|
-
})
|
|
748
|
-
|
|
749
|
-
const testnet = getIsTestnetChainId(originChain.id)
|
|
750
|
-
|
|
751
|
-
const attestation = await waitForAttestation({
|
|
752
|
-
domain: originDomain,
|
|
753
|
-
transactionHash: txHash,
|
|
754
|
-
testnet,
|
|
755
|
-
})
|
|
756
|
-
|
|
757
|
-
if (!attestation) {
|
|
758
|
-
throw new Error("Failed to retrieve attestation")
|
|
759
|
-
}
|
|
760
|
-
|
|
761
|
-
return attestation
|
|
762
|
-
},
|
|
763
|
-
txHash: txHash,
|
|
764
|
-
}
|
|
765
|
-
}
|
|
766
|
-
|
|
767
|
-
export async function burnUSDCToContract({
|
|
768
|
-
walletClient,
|
|
769
|
-
tokenMessenger,
|
|
770
|
-
destinationDomain,
|
|
771
|
-
destinationContract,
|
|
772
|
-
amount,
|
|
773
|
-
burnToken,
|
|
774
|
-
maxFee,
|
|
775
|
-
chain,
|
|
776
|
-
}: {
|
|
777
|
-
walletClient: WalletClient
|
|
778
|
-
tokenMessenger: string
|
|
779
|
-
destinationDomain: number
|
|
780
|
-
destinationContract: string
|
|
781
|
-
amount: bigint
|
|
782
|
-
burnToken: string
|
|
783
|
-
maxFee: bigint
|
|
784
|
-
chain: Chain
|
|
785
|
-
}): Promise<`0x${string}`> {
|
|
786
|
-
const burnData = await getBurnUSDCToContractData({
|
|
787
|
-
tokenMessenger,
|
|
788
|
-
destinationDomain,
|
|
789
|
-
destinationContract,
|
|
790
|
-
amount,
|
|
791
|
-
burnToken,
|
|
792
|
-
maxFee: maxFee,
|
|
793
|
-
})
|
|
794
|
-
|
|
795
|
-
await attemptSwitchChain({
|
|
796
|
-
walletClient,
|
|
797
|
-
desiredChainId: chain.id,
|
|
798
|
-
})
|
|
799
|
-
|
|
800
|
-
const account = walletClient.account?.address
|
|
801
|
-
if (!account) {
|
|
802
|
-
throw new Error("No account found")
|
|
803
|
-
}
|
|
804
|
-
|
|
805
|
-
return walletClient.sendTransaction({
|
|
806
|
-
...burnData,
|
|
807
|
-
account: account as `0x${string}`,
|
|
808
|
-
chain,
|
|
809
|
-
})
|
|
810
|
-
}
|
|
811
|
-
|
|
812
|
-
export async function getBurnUSDCToContractData({
|
|
813
|
-
tokenMessenger,
|
|
814
|
-
destinationDomain,
|
|
815
|
-
destinationContract,
|
|
816
|
-
amount,
|
|
817
|
-
burnToken,
|
|
818
|
-
maxFee,
|
|
819
|
-
}: {
|
|
820
|
-
tokenMessenger: string
|
|
821
|
-
destinationDomain: number
|
|
822
|
-
destinationContract: string
|
|
823
|
-
amount: bigint
|
|
824
|
-
burnToken: string
|
|
825
|
-
maxFee: bigint
|
|
826
|
-
}): Promise<{ to: `0x${string}`; data: `0x${string}`; value: bigint }> {
|
|
827
|
-
logger.console.log("[trails-sdk] get burn USDC to contract data", {
|
|
828
|
-
tokenMessenger,
|
|
829
|
-
destinationDomain,
|
|
830
|
-
destinationContract,
|
|
831
|
-
amount,
|
|
832
|
-
burnToken,
|
|
833
|
-
maxFee,
|
|
834
|
-
})
|
|
835
|
-
|
|
836
|
-
// Format destination contract address as bytes32
|
|
837
|
-
const DESTINATION_CONTRACT_BYTES32 = `0x000000000000000000000000${destinationContract.slice(2)}`
|
|
838
|
-
const DESTINATION_CALLER_BYTES32 =
|
|
839
|
-
"0x0000000000000000000000000000000000000000000000000000000000000000" // Empty bytes32 allows any address to call
|
|
840
|
-
|
|
841
|
-
return {
|
|
842
|
-
to: tokenMessenger as `0x${string}`,
|
|
843
|
-
value: BigInt(0),
|
|
844
|
-
data: encodeFunctionData({
|
|
845
|
-
abi: [
|
|
846
|
-
{
|
|
847
|
-
type: "function",
|
|
848
|
-
name: "depositForBurn",
|
|
849
|
-
stateMutability: "nonpayable",
|
|
850
|
-
inputs: [
|
|
851
|
-
{ name: "amount", type: "uint256" },
|
|
852
|
-
{ name: "destinationDomain", type: "uint32" },
|
|
853
|
-
{ name: "mintRecipient", type: "bytes32" },
|
|
854
|
-
{ name: "burnToken", type: "address" },
|
|
855
|
-
{ name: "destinationCaller", type: "bytes32" },
|
|
856
|
-
{ name: "maxFee", type: "uint256" },
|
|
857
|
-
{ name: "minFinalityThreshold", type: "uint32" },
|
|
858
|
-
],
|
|
859
|
-
outputs: [],
|
|
860
|
-
},
|
|
861
|
-
],
|
|
862
|
-
functionName: "depositForBurn",
|
|
863
|
-
args: [
|
|
864
|
-
amount,
|
|
865
|
-
destinationDomain,
|
|
866
|
-
DESTINATION_CONTRACT_BYTES32 as `0x${string}`,
|
|
867
|
-
burnToken as `0x${string}`,
|
|
868
|
-
DESTINATION_CALLER_BYTES32 as `0x${string}`,
|
|
869
|
-
maxFee,
|
|
870
|
-
1000, // minFinalityThreshold (1000 or less for Fast Transfer)
|
|
871
|
-
],
|
|
872
|
-
}),
|
|
873
|
-
}
|
|
874
|
-
}
|
|
875
|
-
|
|
876
|
-
export async function executeCustomCallWithCCTP({
|
|
877
|
-
relayerClient,
|
|
878
|
-
destinationChain,
|
|
879
|
-
attestation,
|
|
880
|
-
targetContract,
|
|
881
|
-
calldata,
|
|
882
|
-
gasLimit = 500000n,
|
|
883
|
-
}: {
|
|
884
|
-
relayerClient: WalletClient
|
|
885
|
-
destinationChain: Chain
|
|
886
|
-
attestation: Attestation
|
|
887
|
-
targetContract: string
|
|
888
|
-
calldata: `0x${string}`
|
|
889
|
-
gasLimit?: bigint
|
|
890
|
-
}): Promise<`0x${string}`> {
|
|
891
|
-
await attemptSwitchChain({
|
|
892
|
-
walletClient: relayerClient,
|
|
893
|
-
desiredChainId: destinationChain.id,
|
|
894
|
-
})
|
|
895
|
-
|
|
896
|
-
const account = relayerClient.account?.address
|
|
897
|
-
if (!account) {
|
|
898
|
-
throw new Error("No account found")
|
|
899
|
-
}
|
|
900
|
-
|
|
901
|
-
const cctpRelayerAddress = customCctpRelayerAddress[destinationChain.id]
|
|
902
|
-
if (!cctpRelayerAddress) {
|
|
903
|
-
throw new Error("No custom CCTP relayer address found for this chain")
|
|
904
|
-
}
|
|
905
|
-
|
|
906
|
-
const relayData = encodeFunctionData({
|
|
907
|
-
abi: [
|
|
908
|
-
{
|
|
909
|
-
type: "function",
|
|
910
|
-
name: "relayWithCustomCall",
|
|
911
|
-
inputs: [
|
|
912
|
-
{
|
|
913
|
-
type: "tuple",
|
|
914
|
-
name: "request",
|
|
915
|
-
components: [
|
|
916
|
-
{ name: "message", type: "bytes" },
|
|
917
|
-
{ name: "attestation", type: "bytes" },
|
|
918
|
-
{ name: "targetContract", type: "address" },
|
|
919
|
-
{ name: "data", type: "bytes" },
|
|
920
|
-
{ name: "gasLimit", type: "uint256" },
|
|
921
|
-
],
|
|
922
|
-
},
|
|
923
|
-
],
|
|
924
|
-
},
|
|
925
|
-
],
|
|
926
|
-
functionName: "relayWithCustomCall",
|
|
927
|
-
args: [
|
|
928
|
-
{
|
|
929
|
-
message: attestation.message,
|
|
930
|
-
attestation: attestation.attestation,
|
|
931
|
-
targetContract: targetContract as `0x${string}`,
|
|
932
|
-
data: calldata,
|
|
933
|
-
gasLimit,
|
|
934
|
-
},
|
|
935
|
-
],
|
|
936
|
-
})
|
|
937
|
-
|
|
938
|
-
return relayerClient.sendTransaction({
|
|
939
|
-
to: cctpRelayerAddress as `0x${string}`,
|
|
940
|
-
data: relayData,
|
|
941
|
-
account: account as `0x${string}`,
|
|
942
|
-
chain: destinationChain,
|
|
943
|
-
})
|
|
944
|
-
}
|
|
945
|
-
|
|
946
|
-
// Complete flow function
|
|
947
|
-
export async function cctpTransferCaller({
|
|
948
|
-
walletClient,
|
|
949
|
-
relayerClient, // Can be same as walletClient or different
|
|
950
|
-
originChain,
|
|
951
|
-
destinationChain,
|
|
952
|
-
amount,
|
|
953
|
-
targetContract, // The contract you want to call
|
|
954
|
-
calldata, // The function call data
|
|
955
|
-
gasLimit = 500000n,
|
|
956
|
-
}: {
|
|
957
|
-
walletClient: WalletClient
|
|
958
|
-
relayerClient?: WalletClient
|
|
959
|
-
originChain: Chain
|
|
960
|
-
destinationChain: Chain
|
|
961
|
-
amount: bigint
|
|
962
|
-
targetContract: string
|
|
963
|
-
calldata: `0x${string}`
|
|
964
|
-
gasLimit?: bigint
|
|
965
|
-
}): Promise<{
|
|
966
|
-
burnTxHash: `0x${string}`
|
|
967
|
-
executeTxHash: `0x${string}`
|
|
968
|
-
}> {
|
|
969
|
-
// Use walletClient as relayerClient if not provided
|
|
970
|
-
const actualRelayerClient = relayerClient || walletClient
|
|
971
|
-
|
|
972
|
-
logger.console.log("[trails-sdk] Starting CCTP transfer with custom call")
|
|
973
|
-
|
|
974
|
-
// Step 1: Burn USDC on origin chain (send to CCTPRelayer)
|
|
975
|
-
const { waitForAttestation, txHash: burnTxHash } =
|
|
976
|
-
await cctpTransferWithCustomCall({
|
|
977
|
-
walletClient,
|
|
978
|
-
originChain,
|
|
979
|
-
destinationChain,
|
|
980
|
-
amount,
|
|
981
|
-
})
|
|
982
|
-
|
|
983
|
-
logger.console.log("[trails-sdk] Burn transaction sent:", burnTxHash)
|
|
984
|
-
|
|
985
|
-
// Step 2: Wait for attestation
|
|
986
|
-
logger.console.log("[trails-sdk] Waiting for attestation...")
|
|
987
|
-
const attestation = await waitForAttestation()
|
|
988
|
-
|
|
989
|
-
logger.console.log("[trails-sdk] Attestation received, executing custom call")
|
|
990
|
-
|
|
991
|
-
// Step 3: Execute the relayed call on destination chain
|
|
992
|
-
const executeTxHash = await executeCustomCallWithCCTP({
|
|
993
|
-
relayerClient: actualRelayerClient,
|
|
994
|
-
destinationChain,
|
|
995
|
-
attestation,
|
|
996
|
-
targetContract,
|
|
997
|
-
calldata,
|
|
998
|
-
gasLimit,
|
|
999
|
-
})
|
|
1000
|
-
|
|
1001
|
-
logger.console.log("[trails-sdk] Custom call executed:", executeTxHash)
|
|
1002
|
-
|
|
1003
|
-
return {
|
|
1004
|
-
burnTxHash,
|
|
1005
|
-
executeTxHash,
|
|
1006
|
-
}
|
|
1007
|
-
}
|
|
1008
|
-
|
|
1009
|
-
export async function getCCTPRelayerCallData({
|
|
1010
|
-
attestation,
|
|
1011
|
-
targetContract,
|
|
1012
|
-
calldata,
|
|
1013
|
-
gasLimit = 500000n,
|
|
1014
|
-
destinationChain,
|
|
1015
|
-
}: {
|
|
1016
|
-
attestation: Attestation
|
|
1017
|
-
targetContract: string
|
|
1018
|
-
calldata: `0x${string}`
|
|
1019
|
-
gasLimit?: bigint
|
|
1020
|
-
destinationChain: Chain
|
|
1021
|
-
}): Promise<{ to: `0x${string}`; data: `0x${string}`; value: bigint }> {
|
|
1022
|
-
const cctpRelayerAddress = customCctpRelayerAddress[destinationChain.id]
|
|
1023
|
-
if (!cctpRelayerAddress) {
|
|
1024
|
-
throw new Error("No custom CCTP relayer address found for this chain")
|
|
1025
|
-
}
|
|
1026
|
-
|
|
1027
|
-
logger.console.log("[trails-sdk] get CCTP relayer call data", {
|
|
1028
|
-
cctpRelayerAddress,
|
|
1029
|
-
targetContract,
|
|
1030
|
-
gasLimit,
|
|
1031
|
-
})
|
|
1032
|
-
|
|
1033
|
-
const relayData = encodeFunctionData({
|
|
1034
|
-
abi: [
|
|
1035
|
-
{
|
|
1036
|
-
type: "function",
|
|
1037
|
-
name: "relayWithCustomCall",
|
|
1038
|
-
inputs: [
|
|
1039
|
-
{
|
|
1040
|
-
type: "tuple",
|
|
1041
|
-
name: "request",
|
|
1042
|
-
components: [
|
|
1043
|
-
{ name: "message", type: "bytes" },
|
|
1044
|
-
{ name: "attestation", type: "bytes" },
|
|
1045
|
-
{ name: "targetContract", type: "address" },
|
|
1046
|
-
{ name: "data", type: "bytes" },
|
|
1047
|
-
{ name: "gasLimit", type: "uint256" },
|
|
1048
|
-
],
|
|
1049
|
-
},
|
|
1050
|
-
],
|
|
1051
|
-
},
|
|
1052
|
-
],
|
|
1053
|
-
functionName: "relayWithCustomCall",
|
|
1054
|
-
args: [
|
|
1055
|
-
{
|
|
1056
|
-
message: attestation.message,
|
|
1057
|
-
attestation: attestation.attestation,
|
|
1058
|
-
targetContract: targetContract as `0x${string}`,
|
|
1059
|
-
data: calldata,
|
|
1060
|
-
gasLimit,
|
|
1061
|
-
},
|
|
1062
|
-
],
|
|
1063
|
-
})
|
|
1064
|
-
|
|
1065
|
-
return {
|
|
1066
|
-
to: cctpRelayerAddress as `0x${string}`,
|
|
1067
|
-
data: relayData,
|
|
1068
|
-
value: BigInt(0),
|
|
1069
|
-
}
|
|
1070
|
-
}
|