@rozoai/intent-common 0.0.32-beta.1 → 0.0.32-beta.3

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.
Files changed (52) hide show
  1. package/README.md +134 -0
  2. package/dist/api/base.d.ts +1 -1
  3. package/dist/api/base.js +2 -1
  4. package/dist/api/base.js.map +1 -1
  5. package/dist/api/fee.d.ts +2 -1
  6. package/dist/api/fee.js +5 -1
  7. package/dist/api/fee.js.map +1 -1
  8. package/dist/api/new-payment.d.ts +319 -0
  9. package/dist/api/new-payment.js +140 -0
  10. package/dist/api/new-payment.js.map +1 -0
  11. package/dist/api/payment.d.ts +106 -5
  12. package/dist/api/payment.js +117 -10
  13. package/dist/api/payment.js.map +1 -1
  14. package/dist/bridge.d.ts +84 -46
  15. package/dist/bridge.js +183 -169
  16. package/dist/bridge.js.map +1 -1
  17. package/dist/chain.d.ts +6 -0
  18. package/dist/chain.js +38 -14
  19. package/dist/chain.js.map +1 -1
  20. package/dist/daimoPay.d.ts +9 -9
  21. package/dist/daimoPay.js +5 -0
  22. package/dist/daimoPay.js.map +1 -1
  23. package/dist/index.d.ts +2 -0
  24. package/dist/index.js +2 -0
  25. package/dist/index.js.map +1 -1
  26. package/dist/token.d.ts +9 -0
  27. package/dist/token.js +111 -17
  28. package/dist/token.js.map +1 -1
  29. package/dist/validation.d.ts +9 -0
  30. package/dist/validation.js +47 -0
  31. package/dist/validation.js.map +1 -0
  32. package/package.json +4 -2
  33. package/src/api/base.ts +3 -1
  34. package/src/api/fee.ts +8 -2
  35. package/src/api/new-payment.ts +433 -0
  36. package/src/api/payment.ts +172 -13
  37. package/src/bridge.ts +225 -201
  38. package/src/chain.ts +40 -13
  39. package/src/daimoPay.ts +17 -9
  40. package/src/index.ts +2 -0
  41. package/src/token.ts +124 -18
  42. package/src/validation.ts +54 -0
  43. package/test/bridge.test.ts +393 -0
  44. package/dist/chainAddress.d.ts +0 -27
  45. package/dist/chainAddress.js +0 -87
  46. package/dist/chainAddress.js.map +0 -1
  47. package/dist/supportedChain.d.ts +0 -27
  48. package/dist/supportedChain.js +0 -87
  49. package/dist/supportedChain.js.map +0 -1
  50. package/dist/supportedChains.d.ts +0 -27
  51. package/dist/supportedChains.js +0 -87
  52. package/dist/supportedChains.js.map +0 -1
package/src/daimoPay.ts CHANGED
@@ -190,16 +190,16 @@ export type RozoPayDehydratedOrder = {
190
190
  export type RozoPayHydratedOrder = {
191
191
  mode: RozoPayOrderMode.HYDRATED;
192
192
  id: bigint;
193
- intentAddr: Address;
193
+ intentAddr: string;
194
194
  /** Nullable because old intents don't record escrow address. */
195
- escrowContractAddress: Address | null;
195
+ // escrowContractAddress: Address | null;
196
196
  /** Nullable because old intents don't record bridger address. */
197
- bridgerContractAddress: Address | null;
197
+ // bridgerContractAddress: Address | null;
198
198
  /** @deprecated included for backcompat with old versions. Remove soon. */
199
- handoffAddr: Address;
200
- bridgeTokenOutOptions: RozoPayTokenAmount[];
201
- selectedBridgeTokenOutAddr: Address | null;
202
- selectedBridgeTokenOutAmount: bigint | null;
199
+ // handoffAddr: Address;
200
+ // bridgeTokenOutOptions: RozoPayTokenAmount[];
201
+ // selectedBridgeTokenOutAddr: Address | null;
202
+ // selectedBridgeTokenOutAmount: bigint | null;
203
203
  destFinalCallTokenAmount: RozoPayTokenAmount;
204
204
  destFinalCall: OnChainCall;
205
205
  usdValue: number;
@@ -208,7 +208,7 @@ export type RozoPayHydratedOrder = {
208
208
  sourceFulfillerAddr: Address | SolanaPublicKey | StellarPublicKey | null;
209
209
  sourceTokenAmount: RozoPayTokenAmount | null;
210
210
  sourceInitiateTxHash: Hex | null;
211
- sourceStartTxHash: Hex | null;
211
+ // sourceStartTxHash: Hex | null;
212
212
  sourceStatus: RozoPayOrderStatusSource;
213
213
  destStatus: RozoPayOrderStatusDest;
214
214
  destFastFinishTxHash: Hex | null;
@@ -429,6 +429,12 @@ export enum DepositAddressPaymentOptions {
429
429
  STELLAR = "Stellar",
430
430
  WORLD = "Worldchain",
431
431
 
432
+ SOLANA_USDT = "USDT on Solana",
433
+ SOLANA_USDC = "USDC on Solana",
434
+ BASE_USDC = "USDC on Base",
435
+ ETHEREUM_USDT = "USDT on Ethereum",
436
+ ETHEREUM_USDC = "USDC on Ethereum",
437
+
432
438
  /** @deprecated */
433
439
  BITCOIN = "Bitcoin",
434
440
  /** @deprecated */
@@ -449,6 +455,8 @@ export type DepositAddressPaymentOptionMetadata = {
449
455
  id: DepositAddressPaymentOptions;
450
456
  logoURI: string;
451
457
  minimumUsd: number;
458
+ chainId: number;
459
+ token: Token;
452
460
  };
453
461
 
454
462
  export type DepositAddressPaymentOptionData = {
@@ -482,7 +490,7 @@ export interface RozoPayTokenAmount {
482
490
  }
483
491
 
484
492
  export type OnChainCall = {
485
- to: Address;
493
+ to: Address | SolanaPublicKey | StellarPublicKey;
486
494
  data: Hex;
487
495
  value: bigint;
488
496
  };
package/src/index.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export * from "./api/fee";
2
+ export * from "./api/new-payment";
2
3
  export * from "./api/payment";
3
4
  export * from "./assert";
4
5
  export * from "./bridge";
@@ -10,3 +11,4 @@ export * from "./primitiveTypes";
10
11
  export * from "./retryBackoff";
11
12
  export * from "./token";
12
13
  export * from "./try";
14
+ export * from "./validation";
package/src/token.ts CHANGED
@@ -2,10 +2,12 @@ import { Address, getAddress, zeroAddress } from "viem";
2
2
  import { assertNotNull } from "./assert";
3
3
  import {
4
4
  arbitrum,
5
+ avalanche,
5
6
  base,
6
7
  bsc,
7
8
  celo,
8
9
  ethereum,
10
+ gnosis,
9
11
  linea,
10
12
  mantle,
11
13
  optimism,
@@ -716,11 +718,22 @@ export const rozoSolanaUSDC: Token = token({
716
718
  logoURI: TokenLogo.USDC,
717
719
  });
718
720
 
721
+ export const rozoSolanaUSDT: Token = token({
722
+ chainId: rozoSolana.chainId,
723
+ token: "Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB",
724
+ decimals: 6,
725
+ fiatISO: "USD",
726
+ name: "Tether USD",
727
+ symbol: "USDT",
728
+ logoURI: TokenLogo.USDT,
729
+ });
730
+
719
731
  const solanaTokens: Token[] = [
720
732
  solanaUSDC,
721
733
  solanaWSOL,
722
734
  solanaSOL,
723
735
  rozoSolanaUSDC,
736
+ rozoSolanaUSDT,
724
737
  ];
725
738
 
726
739
  //
@@ -748,8 +761,8 @@ export const stellarUSDC: Token = token({
748
761
 
749
762
  export const rozoStellarUSDC: Token = token({
750
763
  chainId: rozoStellar.chainId,
751
- token: "GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN",
752
- decimals: 6,
764
+ token: "USDC:GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN",
765
+ decimals: 7,
753
766
  fiatISO: "USD",
754
767
  name: "USD Coin",
755
768
  symbol: "USDC",
@@ -809,30 +822,109 @@ const worldchainTokens: Token[] = [
809
822
  worldchainWLD,
810
823
  ];
811
824
 
812
- const knownTokensByChain = new Map<number, Token[]>([
813
- [arbitrum.chainId, arbitrumTokens],
814
- [base.chainId, baseTokens],
815
- [bsc.chainId, bscTokens],
816
- [celo.chainId, celoTokens],
817
- [ethereum.chainId, ethereumTokens],
818
- [linea.chainId, lineaTokens],
819
- [mantle.chainId, mantleTokens],
820
- [optimism.chainId, optimismTokens],
821
- [polygon.chainId, polygonTokens],
822
- [rozoSolana.chainId, solanaTokens],
823
- [rozoStellar.chainId, stellarTokens],
824
- [worldchain.chainId, worldchainTokens],
825
+ //
826
+ // Gnosis
827
+ //
828
+
829
+ export const gnosisXDAI = nativeToken({
830
+ chainId: gnosis.chainId,
831
+ name: "xDAI",
832
+ symbol: "XDAI",
833
+ logoURI: TokenLogo.DAI,
834
+ });
835
+
836
+ export const gnosisUSDC: Token = token({
837
+ chainId: gnosis.chainId,
838
+ token: getAddress("0x2a22f9c3b484c3629090feed35f17ff8f88f76f0"),
839
+ decimals: 6,
840
+ fiatISO: "USD",
841
+ name: "USD Coin",
842
+ symbol: "USDC",
843
+ logoURI: TokenLogo.USDC,
844
+ });
845
+
846
+ export const gnosisUSDT: Token = token({
847
+ chainId: gnosis.chainId,
848
+ token: getAddress("0x4ecaba5870353805a9f068101a40e0f32ed605c6"),
849
+ decimals: 6,
850
+ fiatISO: "USD",
851
+ name: "Tether USD",
852
+ symbol: "USDT",
853
+ logoURI: TokenLogo.USDT,
854
+ });
855
+
856
+ const gnosisTokens: Token[] = [gnosisXDAI, gnosisUSDC, gnosisUSDT];
857
+
858
+ //
859
+ // Avalanche
860
+ //
861
+
862
+ export const avalancheAVAX = nativeToken({
863
+ chainId: avalanche.chainId,
864
+ name: "Avalanche",
865
+ symbol: "AVAX",
866
+ logoURI: TokenLogo.AVAX,
867
+ });
868
+
869
+ export const avalancheUSDC: Token = token({
870
+ chainId: avalanche.chainId,
871
+ token: getAddress("0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E"),
872
+ decimals: 6,
873
+ fiatISO: "USD",
874
+ name: "USD Coin",
875
+ symbol: "USDC",
876
+ logoURI: TokenLogo.USDC,
877
+ });
878
+
879
+ export const avalancheUSDT: Token = token({
880
+ chainId: avalanche.chainId,
881
+ token: getAddress("0x9702230a8ea53601f5cd2dc00fdbc13d4df4a8c7"),
882
+ decimals: 6,
883
+ fiatISO: "USD",
884
+ name: "Tether USD",
885
+ symbol: "USDT",
886
+ logoURI: TokenLogo.USDT,
887
+ });
888
+
889
+ const avalancheTokens: Token[] = [avalancheAVAX, avalancheUSDC, avalancheUSDT];
890
+
891
+ /** Support tokens for Rozo Pay */
892
+ export const supportedTokens: Map<number, Token[]> = new Map([
893
+ [arbitrum.chainId, [arbitrumUSDC, arbitrumUSDT]],
894
+ [avalanche.chainId, [avalancheUSDC, avalancheUSDT]],
895
+ [base.chainId, [baseUSDC]],
896
+ [ethereum.chainId, [ethereumUSDC, ethereumUSDT]],
897
+ [gnosis.chainId, [gnosisUSDC, gnosisUSDT]],
898
+ [optimism.chainId, [optimismUSDC, optimismUSDT]],
899
+ [polygon.chainId, [polygonUSDC, polygonUSDT]],
900
+ [rozoSolana.chainId, [rozoSolanaUSDC, rozoSolanaUSDT]],
901
+ [rozoStellar.chainId, [rozoStellarUSDC]],
825
902
  ]);
826
903
 
904
+ // const knownTokensByChain = new Map<number, Token[]>([
905
+ // [arbitrum.chainId, arbitrumTokens],
906
+ // [avalanche.chainId, avalancheTokens],
907
+ // [base.chainId, baseTokens],
908
+ // [bsc.chainId, bscTokens],
909
+ // [celo.chainId, celoTokens],
910
+ // [ethereum.chainId, ethereumTokens],
911
+ // [gnosis.chainId, gnosisTokens],
912
+ // [linea.chainId, lineaTokens],
913
+ // [mantle.chainId, mantleTokens],
914
+ // [optimism.chainId, optimismTokens],
915
+ // [polygon.chainId, polygonTokens],
916
+ // [rozoSolana.chainId, solanaTokens],
917
+ // [rozoStellar.chainId, stellarTokens],
918
+ // [worldchain.chainId, worldchainTokens],
919
+ // ]);
920
+
827
921
  /**
828
922
  * Common tokens, included for convenience.
829
923
  *
830
924
  * Rozo Pay supports payment in many more tokens. In general, the goal for
831
925
  * Pay is to accept all tokens with DEX liquidity on any major chain.
832
926
  */
833
- export const knownTokens: Token[] = Array.from(
834
- knownTokensByChain.values()
835
- ).flat();
927
+ export const knownTokens: Token[] = Array.from(supportedTokens.values()).flat();
836
928
 
837
929
  /* --------------------- Tokens By Address --------------------- */
838
930
 
@@ -982,6 +1074,20 @@ const tokensByChainAndType: Map<
982
1074
  [TokenType.NATIVE_USDC]: rozoStellarUSDC,
983
1075
  },
984
1076
  ],
1077
+ [
1078
+ gnosis.chainId,
1079
+ {
1080
+ [TokenType.NATIVE]: gnosisXDAI,
1081
+ [TokenType.NATIVE_USDC]: gnosisUSDC,
1082
+ },
1083
+ ],
1084
+ [
1085
+ avalanche.chainId,
1086
+ {
1087
+ [TokenType.NATIVE]: avalancheAVAX,
1088
+ [TokenType.NATIVE_USDC]: avalancheUSDC,
1089
+ },
1090
+ ],
985
1091
  [
986
1092
  worldchain.chainId,
987
1093
  {
@@ -0,0 +1,54 @@
1
+ import { PublicKey } from "@solana/web3.js";
2
+ import { Address } from "@stellar/stellar-sdk";
3
+ import { getAddress } from "viem";
4
+ import { Chain, supportedChains } from "./chain";
5
+ import { getKnownToken } from "./token";
6
+
7
+ // Type guards
8
+ export function isChainSupported(
9
+ chainId: number,
10
+ type?: "evm" | "solana" | "stellar"
11
+ ): boolean {
12
+ return supportedChains.some(
13
+ (chain: Chain) =>
14
+ chain.chainId === chainId && (type ? chain.type === type : true)
15
+ );
16
+ }
17
+
18
+ export function isTokenSupported(
19
+ chainId: number,
20
+ tokenAddress: string
21
+ ): boolean {
22
+ return !!getKnownToken(chainId, tokenAddress);
23
+ }
24
+
25
+ // Validation helpers
26
+ export function isValidEvmAddress(address: string): boolean {
27
+ return !!getAddress(address);
28
+ }
29
+
30
+ export function isValidSolanaAddress(address: string): boolean {
31
+ const key = new PublicKey(address);
32
+ return PublicKey.isOnCurve(key.toBytes());
33
+ }
34
+
35
+ export function isValidStellarAddress(address: string): boolean {
36
+ return !!Address.fromString(address);
37
+ }
38
+
39
+ /**
40
+ * Validates that an address matches the expected format for a given chain
41
+ */
42
+ export function validateAddressForChain(
43
+ chainId: number,
44
+ address: string
45
+ ): boolean {
46
+ if (isChainSupported(chainId, "evm")) {
47
+ return !!getAddress(address);
48
+ } else if (isChainSupported(chainId, "solana")) {
49
+ return isValidSolanaAddress(address);
50
+ } else if (isChainSupported(chainId, "stellar")) {
51
+ return isValidStellarAddress(address);
52
+ }
53
+ return false;
54
+ }
@@ -0,0 +1,393 @@
1
+ import test from "tape";
2
+ import { createPaymentBridgeConfig, PaymentBridgeConfig } from "../src/bridge";
3
+ import { base, polygon, rozoSolana, rozoStellar } from "../src/chain";
4
+ import {
5
+ baseUSDC,
6
+ polygonUSDC,
7
+ rozoSolanaUSDC,
8
+ rozoStellarUSDC,
9
+ } from "../src/token";
10
+
11
+ // Valid addresses for testing
12
+ const VALID_EVM_ADDRESS = "0x1a5FdBc891c5D4E6aD68064Ae45D43146D4F9f3a";
13
+ const VALID_SOLANA_ADDRESS = "E35325pbtxCRsA4uVoC3cyBDZy8BMpmxvsvGcHNUa18k";
14
+ const VALID_STELLAR_ADDRESS =
15
+ "GDATMUNQEPN4TPETV47LAKGJELK4DUHHDRPMGD3K5LOHUPXX2DI623KY";
16
+
17
+ test("createPaymentBridgeConfig - Cross-chain payment (Polygon USDC to Base USDC)", (t) => {
18
+ const config: PaymentBridgeConfig = {
19
+ toChain: base.chainId,
20
+ toToken: baseUSDC.token,
21
+ toAddress: VALID_EVM_ADDRESS,
22
+ toUnits: "1", // 1 USDC
23
+ preferredChain: polygon.chainId,
24
+ preferredTokenAddress: polygonUSDC.token,
25
+ };
26
+
27
+ const result = createPaymentBridgeConfig(config);
28
+
29
+ t.equal(
30
+ result.preferred.preferredChain,
31
+ String(polygon.chainId),
32
+ "Preferred chain should be Polygon"
33
+ );
34
+ t.equal(
35
+ result.preferred.preferredToken,
36
+ "USDC",
37
+ "Preferred token should be USDC"
38
+ );
39
+ t.equal(
40
+ result.preferred.preferredTokenAddress,
41
+ polygonUSDC.token,
42
+ "Preferred token address should match Polygon USDC"
43
+ );
44
+
45
+ t.equal(
46
+ result.destination.chainId,
47
+ String(base.chainId),
48
+ "Destination chain should be Base"
49
+ );
50
+ t.equal(
51
+ result.destination.tokenSymbol,
52
+ "USDC",
53
+ "Destination token symbol should be USDC"
54
+ );
55
+ t.equal(
56
+ result.destination.tokenAddress,
57
+ baseUSDC.token,
58
+ "Destination token address should match Base USDC"
59
+ );
60
+ t.equal(
61
+ result.destination.destinationAddress,
62
+ VALID_EVM_ADDRESS,
63
+ "Destination address should match"
64
+ );
65
+ t.equal(result.destination.amountUnits, "1", "Amount units should match");
66
+
67
+ t.equal(
68
+ result.isIntentPayment,
69
+ true,
70
+ "Should be an intent payment (different chains)"
71
+ );
72
+
73
+ t.end();
74
+ });
75
+
76
+ test("createPaymentBridgeConfig - Same-chain payment (Base USDC to Base USDC)", (t) => {
77
+ const config: PaymentBridgeConfig = {
78
+ toChain: base.chainId,
79
+ toToken: baseUSDC.token,
80
+ toAddress: VALID_EVM_ADDRESS,
81
+ toUnits: "1",
82
+ preferredChain: base.chainId,
83
+ preferredTokenAddress: baseUSDC.token,
84
+ };
85
+
86
+ const result = createPaymentBridgeConfig(config);
87
+
88
+ t.equal(
89
+ result.preferred.preferredChain,
90
+ String(base.chainId),
91
+ "Preferred chain should be Base"
92
+ );
93
+ t.equal(
94
+ result.preferred.preferredToken,
95
+ "USDC",
96
+ "Preferred token should be USDC"
97
+ );
98
+
99
+ t.equal(
100
+ result.destination.chainId,
101
+ String(base.chainId),
102
+ "Destination chain should be Base"
103
+ );
104
+
105
+ t.equal(
106
+ result.isIntentPayment,
107
+ false,
108
+ "Should not be an intent payment (same chain and token)"
109
+ );
110
+
111
+ t.end();
112
+ });
113
+
114
+ test("createPaymentBridgeConfig - Payment to Stellar destination", (t) => {
115
+ const config: PaymentBridgeConfig = {
116
+ toChain: rozoStellar.chainId,
117
+ toToken: rozoStellarUSDC.token,
118
+ toAddress: VALID_STELLAR_ADDRESS,
119
+ toUnits: "1", // 1 USDC
120
+ preferredChain: base.chainId,
121
+ preferredTokenAddress: baseUSDC.token,
122
+ };
123
+
124
+ const result = createPaymentBridgeConfig(config);
125
+
126
+ t.equal(
127
+ result.preferred.preferredChain,
128
+ String(base.chainId),
129
+ "Preferred chain should be Base"
130
+ );
131
+ t.equal(
132
+ result.preferred.preferredToken,
133
+ "USDC",
134
+ "Preferred token should be USDC"
135
+ );
136
+
137
+ // Destination should be configured for Stellar
138
+ t.equal(
139
+ result.destination.chainId,
140
+ String(rozoStellarUSDC.chainId),
141
+ "Destination chain should be Stellar"
142
+ );
143
+ t.equal(
144
+ result.destination.tokenSymbol,
145
+ rozoStellarUSDC.symbol,
146
+ "Destination token symbol should be Stellar USDC"
147
+ );
148
+ t.equal(
149
+ result.destination.tokenAddress,
150
+ rozoStellarUSDC.token,
151
+ "Destination token address should match Stellar USDC"
152
+ );
153
+ t.equal(
154
+ result.destination.destinationAddress,
155
+ VALID_STELLAR_ADDRESS,
156
+ "Destination address should match Stellar address"
157
+ );
158
+
159
+ t.equal(
160
+ result.isIntentPayment,
161
+ true,
162
+ "Should be an intent payment (Base to Stellar)"
163
+ );
164
+
165
+ t.end();
166
+ });
167
+
168
+ test("createPaymentBridgeConfig - Payment to Solana destination", (t) => {
169
+ const config: PaymentBridgeConfig = {
170
+ toChain: rozoSolana.chainId,
171
+ toToken: rozoSolanaUSDC.token,
172
+ toAddress: VALID_SOLANA_ADDRESS,
173
+ toUnits: "1", // 1 USDC
174
+ preferredChain: base.chainId,
175
+ preferredTokenAddress: baseUSDC.token,
176
+ };
177
+
178
+ const result = createPaymentBridgeConfig(config);
179
+
180
+ t.equal(
181
+ result.preferred.preferredChain,
182
+ String(base.chainId),
183
+ "Preferred chain should be Base"
184
+ );
185
+ t.equal(
186
+ result.preferred.preferredToken,
187
+ "USDC",
188
+ "Preferred token should be USDC"
189
+ );
190
+
191
+ // Destination should be configured for Solana
192
+ t.equal(
193
+ result.destination.chainId,
194
+ String(rozoSolanaUSDC.chainId),
195
+ "Destination chain should be Solana"
196
+ );
197
+ t.equal(
198
+ result.destination.tokenSymbol,
199
+ rozoSolanaUSDC.symbol,
200
+ "Destination token symbol should be Solana USDC"
201
+ );
202
+ t.equal(
203
+ result.destination.tokenAddress,
204
+ rozoSolanaUSDC.token,
205
+ "Destination token address should match Solana USDC"
206
+ );
207
+ t.equal(
208
+ result.destination.destinationAddress,
209
+ VALID_SOLANA_ADDRESS,
210
+ "Destination address should match Solana address"
211
+ );
212
+
213
+ t.equal(
214
+ result.isIntentPayment,
215
+ true,
216
+ "Should be an intent payment (Base to Solana)"
217
+ );
218
+
219
+ t.end();
220
+ });
221
+
222
+ test("createPaymentBridgeConfig - Error: Unsupported token", (t) => {
223
+ const config: PaymentBridgeConfig = {
224
+ toChain: base.chainId,
225
+ toToken: "0x0000000000000000000000000000000000000000", // Invalid token
226
+ toAddress: VALID_EVM_ADDRESS,
227
+ toUnits: "1",
228
+ preferredChain: base.chainId,
229
+ preferredTokenAddress: baseUSDC.token,
230
+ };
231
+
232
+ t.throws(
233
+ () => createPaymentBridgeConfig(config),
234
+ /(Unsupported token|or token)/,
235
+ "Should throw error for unsupported token"
236
+ );
237
+
238
+ t.end();
239
+ });
240
+
241
+ test("createPaymentBridgeConfig - Error: Invalid address for chain", (t) => {
242
+ const config: PaymentBridgeConfig = {
243
+ toChain: base.chainId,
244
+ toToken: baseUSDC.token,
245
+ toAddress: "invalid-address",
246
+ toUnits: "1",
247
+ preferredChain: base.chainId,
248
+ preferredTokenAddress: baseUSDC.token,
249
+ };
250
+
251
+ t.throws(
252
+ () => createPaymentBridgeConfig(config),
253
+ (err: any) => {
254
+ // viem's getAddress throws InvalidAddressError when address is invalid
255
+ // The error will be thrown from validateAddressForChain before the bridge function can catch it
256
+ return (
257
+ err.name === "InvalidAddressError" ||
258
+ err.message?.includes("Address") ||
259
+ err.message?.includes("invalid")
260
+ );
261
+ },
262
+ "Should throw error for invalid address"
263
+ );
264
+
265
+ t.end();
266
+ });
267
+
268
+ test("createPaymentBridgeConfig - Error: Unsupported preferred token", (t) => {
269
+ const config: PaymentBridgeConfig = {
270
+ toChain: base.chainId,
271
+ toToken: baseUSDC.token,
272
+ toAddress: VALID_EVM_ADDRESS,
273
+ toUnits: "1",
274
+ preferredChain: polygon.chainId,
275
+ preferredTokenAddress: "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", // Truly invalid token address
276
+ };
277
+
278
+ t.throws(
279
+ () => createPaymentBridgeConfig(config),
280
+ /Unknown token/,
281
+ "Should throw error for unsupported preferred token"
282
+ );
283
+
284
+ t.end();
285
+ });
286
+
287
+ test("createPaymentBridgeConfig - Error: Unsupported chain or token", (t) => {
288
+ const config: PaymentBridgeConfig = {
289
+ toChain: 99999, // Unsupported chain
290
+ toToken: baseUSDC.token,
291
+ toAddress: VALID_EVM_ADDRESS,
292
+ toUnits: "1",
293
+ preferredChain: base.chainId,
294
+ preferredTokenAddress: baseUSDC.token,
295
+ };
296
+
297
+ t.throws(
298
+ () => createPaymentBridgeConfig(config),
299
+ /Unknown chainId/,
300
+ "Should throw error for unsupported chain"
301
+ );
302
+
303
+ t.end();
304
+ });
305
+
306
+ test("createPaymentBridgeConfig - Intent payment detection: different chain, same token", (t) => {
307
+ const config: PaymentBridgeConfig = {
308
+ toChain: base.chainId,
309
+ toToken: baseUSDC.token,
310
+ toAddress: VALID_EVM_ADDRESS,
311
+ toUnits: "1",
312
+ preferredChain: polygon.chainId,
313
+ preferredTokenAddress: polygonUSDC.token,
314
+ };
315
+
316
+ const result = createPaymentBridgeConfig(config);
317
+
318
+ t.equal(
319
+ result.isIntentPayment,
320
+ true,
321
+ "Should be intent payment when chains differ"
322
+ );
323
+
324
+ t.end();
325
+ });
326
+
327
+ test("createPaymentBridgeConfig - Intent payment detection: same chain, different token", (t) => {
328
+ // This test assumes there are different tokens on the same chain
329
+ // For Base, we'll use the same token but the logic should still work
330
+ const config: PaymentBridgeConfig = {
331
+ toChain: base.chainId,
332
+ toToken: baseUSDC.token,
333
+ toAddress: VALID_EVM_ADDRESS,
334
+ toUnits: "1",
335
+ preferredChain: base.chainId,
336
+ preferredTokenAddress: baseUSDC.token,
337
+ };
338
+
339
+ const result = createPaymentBridgeConfig(config);
340
+
341
+ // When same chain and token, preferredTokenAddress will be set but
342
+ // the comparison checks preferredToken (symbol) vs toToken (address)
343
+ // So this might still be false if symbols match
344
+ t.equal(
345
+ result.isIntentPayment,
346
+ false,
347
+ "Should not be intent payment when same chain and token"
348
+ );
349
+
350
+ t.end();
351
+ });
352
+
353
+ test("createPaymentBridgeConfig - Large amount units", (t) => {
354
+ const config: PaymentBridgeConfig = {
355
+ toChain: base.chainId,
356
+ toToken: baseUSDC.token,
357
+ toAddress: VALID_EVM_ADDRESS,
358
+ toUnits: "1000000", // 1 million USDC
359
+ preferredChain: polygon.chainId,
360
+ preferredTokenAddress: polygonUSDC.token,
361
+ };
362
+
363
+ const result = createPaymentBridgeConfig(config);
364
+
365
+ t.equal(
366
+ result.destination.amountUnits,
367
+ "1000000",
368
+ "Should handle large amount units correctly"
369
+ );
370
+
371
+ t.end();
372
+ });
373
+
374
+ test("createPaymentBridgeConfig - Zero amount units", (t) => {
375
+ const config: PaymentBridgeConfig = {
376
+ toChain: base.chainId,
377
+ toToken: baseUSDC.token,
378
+ toAddress: VALID_EVM_ADDRESS,
379
+ toUnits: "0",
380
+ preferredChain: base.chainId,
381
+ preferredTokenAddress: baseUSDC.token,
382
+ };
383
+
384
+ const result = createPaymentBridgeConfig(config);
385
+
386
+ t.equal(
387
+ result.destination.amountUnits,
388
+ "0",
389
+ "Should handle zero amount units correctly"
390
+ );
391
+
392
+ t.end();
393
+ });