@shogun-sdk/intents-sdk 1.1.0 → 1.2.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.
Files changed (118) hide show
  1. package/dist/esm/constants.js +24 -8
  2. package/dist/esm/constants.js.map +1 -1
  3. package/dist/esm/core/evm/intent-provider.js +55 -2
  4. package/dist/esm/core/evm/intent-provider.js.map +1 -1
  5. package/dist/esm/core/evm/order-signature.js +12 -1
  6. package/dist/esm/core/evm/order-signature.js.map +1 -1
  7. package/dist/esm/core/evm/permit2.js +24 -1
  8. package/dist/esm/core/evm/permit2.js.map +1 -1
  9. package/dist/esm/core/evm/sdk.js +5 -18
  10. package/dist/esm/core/evm/sdk.js.map +1 -1
  11. package/dist/esm/core/evm/validator.js +21 -0
  12. package/dist/esm/core/evm/validator.js.map +1 -1
  13. package/dist/esm/core/orders/api/index.js +31 -0
  14. package/dist/esm/core/orders/api/index.js.map +1 -0
  15. package/dist/esm/core/orders/cross-chain.js +3 -0
  16. package/dist/esm/core/orders/cross-chain.js.map +1 -1
  17. package/dist/esm/core/orders/dca-single-chain.js +169 -0
  18. package/dist/esm/core/orders/dca-single-chain.js.map +1 -0
  19. package/dist/esm/core/orders/single-chain.js +4 -0
  20. package/dist/esm/core/orders/single-chain.js.map +1 -1
  21. package/dist/esm/core/sdk.js +55 -81
  22. package/dist/esm/core/sdk.js.map +1 -1
  23. package/dist/esm/core/solana/dca/cancel-order.js +61 -0
  24. package/dist/esm/core/solana/dca/cancel-order.js.map +1 -0
  25. package/dist/esm/core/solana/dca/create-order.js +82 -0
  26. package/dist/esm/core/solana/dca/create-order.js.map +1 -0
  27. package/dist/esm/core/solana/inspect.js +43 -0
  28. package/dist/esm/core/solana/inspect.js.map +1 -0
  29. package/dist/esm/core/solana/sdk.js +36 -18
  30. package/dist/esm/core/solana/sdk.js.map +1 -1
  31. package/dist/esm/core/solana/utils.js.map +1 -1
  32. package/dist/esm/core/solana/validator.js +3 -0
  33. package/dist/esm/core/solana/validator.js.map +1 -1
  34. package/dist/esm/core/sui/sdk.js +0 -21
  35. package/dist/esm/core/sui/sdk.js.map +1 -1
  36. package/dist/esm/core/sui/validator.js +4 -0
  37. package/dist/esm/core/sui/validator.js.map +1 -1
  38. package/dist/esm/index.js +4 -1
  39. package/dist/esm/index.js.map +1 -1
  40. package/dist/esm/utils/base-validator.js +28 -0
  41. package/dist/esm/utils/base-validator.js.map +1 -1
  42. package/dist/esm/utils/order-validator.js +31 -0
  43. package/dist/esm/utils/order-validator.js.map +1 -1
  44. package/dist/esm/utils/quote/liquidswap.js.map +1 -1
  45. package/dist/types/constants.d.ts +10 -7
  46. package/dist/types/constants.d.ts.map +1 -1
  47. package/dist/types/core/evm/intent-provider.d.ts +5 -2
  48. package/dist/types/core/evm/intent-provider.d.ts.map +1 -1
  49. package/dist/types/core/evm/order-signature.d.ts +5 -0
  50. package/dist/types/core/evm/order-signature.d.ts.map +1 -1
  51. package/dist/types/core/evm/permit2.d.ts +90 -0
  52. package/dist/types/core/evm/permit2.d.ts.map +1 -1
  53. package/dist/types/core/evm/sdk.d.ts +3 -5
  54. package/dist/types/core/evm/sdk.d.ts.map +1 -1
  55. package/dist/types/core/evm/validator.d.ts +4 -0
  56. package/dist/types/core/evm/validator.d.ts.map +1 -1
  57. package/dist/types/core/orders/api/index.d.ts +8 -0
  58. package/dist/types/core/orders/api/index.d.ts.map +1 -0
  59. package/dist/types/core/orders/cross-chain.d.ts.map +1 -1
  60. package/dist/types/core/orders/dca-single-chain.d.ts +47 -0
  61. package/dist/types/core/orders/dca-single-chain.d.ts.map +1 -0
  62. package/dist/types/core/orders/single-chain.d.ts.map +1 -1
  63. package/dist/types/core/sdk.d.ts +16 -12
  64. package/dist/types/core/sdk.d.ts.map +1 -1
  65. package/dist/types/core/solana/dca/cancel-order.d.ts +5 -0
  66. package/dist/types/core/solana/dca/cancel-order.d.ts.map +1 -0
  67. package/dist/types/core/solana/dca/create-order.d.ts +8 -0
  68. package/dist/types/core/solana/dca/create-order.d.ts.map +1 -0
  69. package/dist/types/core/solana/inspect.d.ts +14 -0
  70. package/dist/types/core/solana/inspect.d.ts.map +1 -0
  71. package/dist/types/core/solana/sdk.d.ts +4 -5
  72. package/dist/types/core/solana/sdk.d.ts.map +1 -1
  73. package/dist/types/core/solana/utils.d.ts +2 -1
  74. package/dist/types/core/solana/utils.d.ts.map +1 -1
  75. package/dist/types/core/solana/validator.d.ts +1 -0
  76. package/dist/types/core/solana/validator.d.ts.map +1 -1
  77. package/dist/types/core/sui/sdk.d.ts +1 -6
  78. package/dist/types/core/sui/sdk.d.ts.map +1 -1
  79. package/dist/types/core/sui/validator.d.ts +1 -0
  80. package/dist/types/core/sui/validator.d.ts.map +1 -1
  81. package/dist/types/index.d.ts +4 -1
  82. package/dist/types/index.d.ts.map +1 -1
  83. package/dist/types/types/intent.d.ts +22 -0
  84. package/dist/types/types/intent.d.ts.map +1 -1
  85. package/dist/types/utils/base-validator.d.ts +7 -0
  86. package/dist/types/utils/base-validator.d.ts.map +1 -1
  87. package/dist/types/utils/order-validator.d.ts +15 -0
  88. package/dist/types/utils/order-validator.d.ts.map +1 -1
  89. package/package.json +1 -1
  90. package/src/auth/siwe.ts +1 -1
  91. package/src/constants.ts +72 -39
  92. package/src/core/evm/connectors/hyperevm.ts +1 -1
  93. package/src/core/evm/intent-provider.ts +88 -4
  94. package/src/core/evm/order-signature.ts +23 -1
  95. package/src/core/evm/permit2.ts +47 -1
  96. package/src/core/evm/sdk.ts +15 -21
  97. package/src/core/evm/validator.ts +32 -0
  98. package/src/core/orders/api/index.ts +33 -0
  99. package/src/core/orders/cross-chain.ts +4 -0
  100. package/src/core/orders/dca-single-chain.ts +143 -0
  101. package/src/core/orders/single-chain.ts +6 -0
  102. package/src/core/sdk.ts +73 -102
  103. package/src/core/solana/cancel-order.ts +1 -1
  104. package/src/core/solana/dca/cancel-order.ts +91 -0
  105. package/src/core/solana/dca/create-order.ts +142 -0
  106. package/src/core/solana/inspect.ts +50 -0
  107. package/src/core/solana/order-instructions.ts +1 -1
  108. package/src/core/solana/sdk.ts +65 -21
  109. package/src/core/solana/utils.ts +2 -1
  110. package/src/core/solana/validator.ts +4 -0
  111. package/src/core/sui/sdk.ts +2 -24
  112. package/src/core/sui/validator.ts +5 -0
  113. package/src/index.ts +13 -1
  114. package/src/types/api.ts +1 -1
  115. package/src/types/intent.ts +27 -0
  116. package/src/utils/base-validator.ts +40 -0
  117. package/src/utils/order-validator.ts +38 -0
  118. package/src/utils/quote/liquidswap.ts +2 -2
package/src/constants.ts CHANGED
@@ -1,69 +1,101 @@
1
- import { address, type Address as SolanaAddress } from '@solana/kit';
2
- import type { Address, Hex } from 'viem';
1
+ import { type Address as SolanaAddress } from '@solana/kit';
2
+ import type { Address as EvmAddress } from 'viem';
3
3
  import type { SupportedChain, SupportedEvmChain, SupportedSuiChain } from './chains.js';
4
4
  import { ChainID } from './chains.js';
5
5
 
6
- const useProdConfig = true;
6
+ const useProdConfig = false;
7
7
 
8
8
  export enum OrderType {
9
9
  CROSS_CHAIN_LIMIT = 'CROSS_CHAIN_LIMIT',
10
10
  SINGLE_CHAIN_LIMIT = 'SINGLE_CHAIN_LIMIT',
11
11
  }
12
12
 
13
- export const PERMIT2_ADDRESS: Record<SupportedEvmChain, Address> = {
14
- [ChainID.Arbitrum]: '0x000000000022d473030f116ddee9f6b43ac78ba3' as Address,
15
- [ChainID.Optimism]: '0x000000000022d473030f116ddee9f6b43ac78ba3' as Address,
16
- [ChainID.Base]: '0x000000000022d473030f116ddee9f6b43ac78ba3' as Address,
17
- [ChainID.Hyperliquid]: '0x000000000022d473030f116ddee9f6b43ac78ba3' as Address,
13
+ export const PERMIT2_ADDRESS: Record<SupportedEvmChain, EvmAddress> = {
14
+ [ChainID.Arbitrum]: '0x000000000022d473030f116ddee9f6b43ac78ba3' as EvmAddress,
15
+ [ChainID.Optimism]: '0x000000000022d473030f116ddee9f6b43ac78ba3' as EvmAddress,
16
+ [ChainID.Base]: '0x000000000022d473030f116ddee9f6b43ac78ba3' as EvmAddress,
17
+ [ChainID.Hyperliquid]: '0x000000000022d473030f116ddee9f6b43ac78ba3' as EvmAddress,
18
18
  };
19
19
 
20
20
  // Production guard addresses
21
- export const PROD_CROSS_CHAIN_GUARD_ADDRESSES: Record<SupportedChain, SolanaAddress | Hex> = {
22
- [ChainID.Arbitrum]: '0x921ab91ee3dc38fd47aefd6218fd6b0bd9d64c18' as Address,
23
- [ChainID.Optimism]: '0xa89b2aae94769bf4a5dc9982eabe2c513564d1ea' as Address,
24
- [ChainID.Base]: '0x5173e9f12e2485f990ce4dac877d8545fb4ec81a' as Address,
25
- [ChainID.Hyperliquid]: '0x6595bcc70e0c34ecf9174bb72881eea89b075021' as Address,
26
- [ChainID.Solana]: address('4kD6otCKkKZQej8YNQkWdzG9xMo5XYN8T1BbUrJUE63E'),
21
+ export const PROD_CROSS_CHAIN_GUARD_ADDRESSES: Record<SupportedChain, SolanaAddress | EvmAddress> = {
22
+ [ChainID.Arbitrum]: '0x921ab91ee3dc38fd47aefd6218fd6b0bd9d64c18' as EvmAddress,
23
+ [ChainID.Optimism]: '0xa89b2aae94769bf4a5dc9982eabe2c513564d1ea' as EvmAddress,
24
+ [ChainID.Base]: '0x5173e9f12e2485f990ce4dac877d8545fb4ec81a' as EvmAddress,
25
+ [ChainID.Hyperliquid]: '0x6595bcc70e0c34ecf9174bb72881eea89b075021' as EvmAddress,
26
+ [ChainID.Solana]:
27
+ '4kD6otCKkKZQej8YNQkWdzG9xMo5XYN8T1BbUrJUE63E' as SolanaAddress<'4kD6otCKkKZQej8YNQkWdzG9xMo5XYN8T1BbUrJUE63E'>,
27
28
  [ChainID.Sui]: '0x88a47715f41c5e47ff6d6dcfc0285f3802ae1587a9f411d0daff20269245a1b0',
28
29
  };
29
30
 
30
31
  // Test guard addresses
31
- export const TEST_CROSS_CHAIN_GUARD_ADDRESSES: Record<SupportedChain, SolanaAddress | Hex> = {
32
- [ChainID.Arbitrum]: '0x8cfacbe4e236c0ca0a7b4b9fc5b696aa9f07cc41' as Address,
33
- [ChainID.Optimism]: '0x66fbcccbd3ffe2d347bc2776ef8a6561ff29998e' as Address,
34
- [ChainID.Base]: '0xa895a53634854756ae8e902065e3be26ee36381d' as Address,
35
- [ChainID.Hyperliquid]: '0xB8ec423b5f03b138Ad47c85Ea5F7AAEBB116D65e' as Address,
36
- [ChainID.Solana]: address('FAKnXTY7cNz9wMTpH1wwsU9a1xzBAiMwAn2NXmGMLynP'),
32
+ export const TEST_CROSS_CHAIN_GUARD_ADDRESSES: Record<SupportedChain, SolanaAddress | EvmAddress> = {
33
+ [ChainID.Arbitrum]: '0x8cfacbe4e236c0ca0a7b4b9fc5b696aa9f07cc41' as EvmAddress,
34
+ [ChainID.Optimism]: '0x66fbcccbd3ffe2d347bc2776ef8a6561ff29998e' as EvmAddress,
35
+ [ChainID.Base]: '0xa895a53634854756ae8e902065e3be26ee36381d' as EvmAddress,
36
+ [ChainID.Hyperliquid]: '0xB8ec423b5f03b138Ad47c85Ea5F7AAEBB116D65e' as EvmAddress,
37
+ [ChainID.Solana]:
38
+ 'FAKnXTY7cNz9wMTpH1wwsU9a1xzBAiMwAn2NXmGMLynP' as SolanaAddress<'FAKnXTY7cNz9wMTpH1wwsU9a1xzBAiMwAn2NXmGMLynP'>,
37
39
  [ChainID.Sui]: '0xd5057237ba5fe3d9aa00bb2a1555c3d3de77fd5653e72ce70f4aa0561fa9f04d',
38
40
  };
39
41
 
40
42
  // Prod guard addresses
41
- export const PROD_SINGLE_CHAIN_GUARD_ADDRESSES: Record<Exclude<ChainID, SupportedSuiChain>, SolanaAddress | Hex> = {
42
- [ChainID.Arbitrum]: '0x0' as Address,
43
- [ChainID.Optimism]: '0x0' as Address,
44
- [ChainID.Base]: '0x449808a27f42518c57791d4c2c6626f0069a0ed9' as Address,
45
- [ChainID.Hyperliquid]: '0x618ee9b6829261108d811b91581e00da192b0b75' as Address,
46
- [ChainID.Solana]: address('CwWkajHjBCWMKZdwR5D1P5M6Fgf9dAyxqZ1qDZgZGo1e'),
43
+ export const PROD_SINGLE_CHAIN_GUARD_ADDRESSES: Record<
44
+ Exclude<ChainID, SupportedSuiChain>,
45
+ SolanaAddress | EvmAddress
46
+ > = {
47
+ [ChainID.Arbitrum]: '0x0' as EvmAddress,
48
+ [ChainID.Optimism]: '0x0' as EvmAddress,
49
+ [ChainID.Base]: '0x449808a27f42518c57791d4c2c6626f0069a0ed9' as EvmAddress,
50
+ [ChainID.Hyperliquid]: '0x618ee9b6829261108d811b91581e00da192b0b75' as EvmAddress,
51
+ [ChainID.Solana]:
52
+ 'CwWkajHjBCWMKZdwR5D1P5M6Fgf9dAyxqZ1qDZgZGo1e' as SolanaAddress<'CwWkajHjBCWMKZdwR5D1P5M6Fgf9dAyxqZ1qDZgZGo1e'>,
47
53
  };
48
54
 
49
55
  // Test guard addresses
50
- export const TEST_SINGLE_CHAIN_GUARD_ADDRESSES: Record<Exclude<ChainID, SupportedSuiChain>, SolanaAddress | Hex> = {
51
- [ChainID.Arbitrum]: '0x0' as Address,
52
- [ChainID.Optimism]: '0x0' as Address,
53
- [ChainID.Base]: '0x5602cedc725564694162a320c5bb91b5b4b25fca' as Address,
54
- [ChainID.Hyperliquid]: '0x6a2f41f905035fa89b7329c0661061b580b1eb92' as Address,
55
- [ChainID.Solana]: address('4wi5wsgSs9q9WV55s9AvKWTdeMNPcDXhzcbx5SoRRFcg'),
56
+ export const TEST_SINGLE_CHAIN_GUARD_ADDRESSES: Record<
57
+ Exclude<ChainID, SupportedSuiChain>,
58
+ SolanaAddress | EvmAddress
59
+ > = {
60
+ [ChainID.Arbitrum]: '0x0' as EvmAddress,
61
+ [ChainID.Optimism]: '0x0' as EvmAddress,
62
+ [ChainID.Base]: '0x5602cedc725564694162a320c5bb91b5b4b25fca' as EvmAddress,
63
+ [ChainID.Hyperliquid]: '0x6a2f41f905035fa89b7329c0661061b580b1eb92' as EvmAddress,
64
+ [ChainID.Solana]:
65
+ '4wi5wsgSs9q9WV55s9AvKWTdeMNPcDXhzcbx5SoRRFcg' as SolanaAddress<'4wi5wsgSs9q9WV55s9AvKWTdeMNPcDXhzcbx5SoRRFcg'>,
66
+ };
67
+
68
+ export const PROD_DCA_SINGLE_CHAIN_GUARD_ADDRESSES: Record<SupportedEvmChain, EvmAddress> = {
69
+ [ChainID.Arbitrum]: '0x0000000000000000000000000000000000000000' as EvmAddress,
70
+ [ChainID.Optimism]: '0x0000000000000000000000000000000000000000' as EvmAddress,
71
+ [ChainID.Base]: '0x1ab119e95a87ff59a57eba6dec6599aff8b2d7e4' as EvmAddress,
72
+ [ChainID.Hyperliquid]: '0x490911e5236f68ba921cbecc4b82b5e01b745731' as EvmAddress,
73
+ };
74
+
75
+ // Test guard addresses
76
+ export const TEST_DCA_SINGLE_CHAIN_GUARD_ADDRESSES: Record<SupportedEvmChain, EvmAddress> = {
77
+ [ChainID.Arbitrum]: '0x0000000000000000000000000000000000000000' as EvmAddress,
78
+ [ChainID.Optimism]: '0x0000000000000000000000000000000000000000' as EvmAddress,
79
+ [ChainID.Base]: '0x79fd6a458066396c8938823049b6e614ce6b71f2' as EvmAddress,
80
+ [ChainID.Hyperliquid]: '0x5156e6c7b7a1c60ed4d6b2175f88f1f6875c78d8' as EvmAddress,
56
81
  };
57
82
 
58
83
  export const CROSS_CHAIN_GUARD_ADDRESSES = useProdConfig
59
84
  ? PROD_CROSS_CHAIN_GUARD_ADDRESSES
60
85
  : TEST_CROSS_CHAIN_GUARD_ADDRESSES;
86
+
61
87
  export const SINGLE_CHAIN_GUARD_ADDRESSES = useProdConfig
62
88
  ? PROD_SINGLE_CHAIN_GUARD_ADDRESSES
63
89
  : TEST_SINGLE_CHAIN_GUARD_ADDRESSES;
64
90
 
65
- export const NATIVE_SOLANA_TOKEN_ADDRESS = address('So11111111111111111111111111111111111111111');
66
- export const WRAPPED_SOL_MINT_ADDRESS = address('So11111111111111111111111111111111111111112');
91
+ export const DCA_SINGLE_CHAIN_GUARD_ADDRESSES = useProdConfig
92
+ ? PROD_DCA_SINGLE_CHAIN_GUARD_ADDRESSES
93
+ : TEST_DCA_SINGLE_CHAIN_GUARD_ADDRESSES;
94
+
95
+ export const NATIVE_SOLANA_TOKEN_ADDRESS =
96
+ 'So11111111111111111111111111111111111111111' as SolanaAddress<'So11111111111111111111111111111111111111111'>;
97
+ export const WRAPPED_SOL_MINT_ADDRESS =
98
+ 'So11111111111111111111111111111111111111112' as SolanaAddress<'So11111111111111111111111111111111111111112'>;
67
99
 
68
100
  type SolanaMint = {
69
101
  name: string;
@@ -109,14 +141,15 @@ export const NATIVE_EVM_ETH_ADDRESSES = [
109
141
  '0x0000000000000000000000000000000000000000',
110
142
  ];
111
143
 
112
- export const WRAPPED_ETH_ADDRESSES: Record<SupportedEvmChain, Address> = {
113
- [ChainID.Arbitrum]: '0x82af49447d8a07e3bd95bd0d56f35241523fbab1' as Address,
114
- [ChainID.Optimism]: '0x4200000000000000000000000000000000000006' as Address,
115
- [ChainID.Base]: '0x4200000000000000000000000000000000000006' as Address,
116
- [ChainID.Hyperliquid]: '0x5555555555555555555555555555555555555555' as Address,
144
+ export const WRAPPED_ETH_ADDRESSES: Record<SupportedEvmChain, EvmAddress> = {
145
+ [ChainID.Arbitrum]: '0x82af49447d8a07e3bd95bd0d56f35241523fbab1' as EvmAddress,
146
+ [ChainID.Optimism]: '0x4200000000000000000000000000000000000006' as EvmAddress,
147
+ [ChainID.Base]: '0x4200000000000000000000000000000000000006' as EvmAddress,
148
+ [ChainID.Hyperliquid]: '0x5555555555555555555555555555555555555555' as EvmAddress,
117
149
  };
118
150
 
119
151
  export const NATIVE_SUI_TOKEN_ADDRESS = '0x0000000000000000000000000000000000000000000000000000000000000002::sui::SUI';
152
+
120
153
  export function isNativeEvmToken(tokenAddress: string): boolean {
121
154
  const normalizedAddress = tokenAddress.toLowerCase();
122
155
 
@@ -16,7 +16,7 @@ export const hyperEVM = defineChain({
16
16
  blockExplorers: {
17
17
  default: {
18
18
  name: 'HyperEVM Explorer',
19
- url: 'https://hyperevmscan.io/',
19
+ url: 'https://hyperevmscan.io/',
20
20
  },
21
21
  },
22
22
  testnet: false,
@@ -1,13 +1,32 @@
1
1
  import { type Address } from 'viem';
2
2
  import { type SupportedEvmChain } from '../../chains.js';
3
3
  import type { EVMConfig } from '../../config.js';
4
- import { SINGLE_CHAIN_GUARD_ADDRESSES, CROSS_CHAIN_GUARD_ADDRESSES, MAX_UINT_32 } from '../../constants.js';
5
- import { getEVMCrossChainOrderTypedData, getEVMSingleChainOrderTypedData } from './order-signature.js';
6
- import { type CrossChainPermitTransferFrom, type SingleChainPermitTransferFrom, type TransferData } from './permit2.js';
4
+ import {
5
+ SINGLE_CHAIN_GUARD_ADDRESSES,
6
+ CROSS_CHAIN_GUARD_ADDRESSES,
7
+ MAX_UINT_32,
8
+ DCA_SINGLE_CHAIN_GUARD_ADDRESSES,
9
+ } from '../../constants.js';
10
+ import {
11
+ getEVMCrossChainOrderTypedData,
12
+ getEVMDcaSingleChainOrderTypedData,
13
+ getEVMSingleChainOrderTypedData,
14
+ } from './order-signature.js';
15
+ import {
16
+ type CrossChainPermitTransferFrom,
17
+ type DcaSingleChainPermitTransferFrom,
18
+ type SingleChainPermitTransferFrom,
19
+ type TransferData,
20
+ } from './permit2.js';
7
21
  import { ChainProvider } from './chain-provider.js';
8
22
  import type { CrossChainOrder } from '../orders/cross-chain.js';
9
23
  import type { SingleChainOrder } from '../orders/single-chain.js';
10
- import type { CrossChainOrderPrepared, SingleChainOrderPrepared } from '../../types/intent.js';
24
+ import type {
25
+ CrossChainOrderPrepared,
26
+ DcaSingleChainOrderPrepared,
27
+ SingleChainOrderPrepared,
28
+ } from '../../types/intent.js';
29
+ import type { DcaSingleChainOrder } from '../orders/dca-single-chain.js';
11
30
 
12
31
  export class EVMIntentProvider {
13
32
  provider: ChainProvider;
@@ -119,4 +138,69 @@ export class EVMIntentProvider {
119
138
  static getRandomNonce(): bigint {
120
139
  return BigInt(Math.floor(Math.random() * Number(MAX_UINT_32)));
121
140
  }
141
+
142
+ async prepareDcaSingleChainOrder(order: DcaSingleChainOrder): Promise<DcaSingleChainOrderPrepared> {
143
+ const { orderTypedData: dataToSign, nonce } = await getEVMDcaSingleChainOrderTypedData(order);
144
+
145
+ const signature = await this.provider.walletClient.signTypedData({
146
+ message: dataToSign.message,
147
+ primaryType: dataToSign.primaryType,
148
+ types: dataToSign.types,
149
+ domain: dataToSign.domain,
150
+ account: this.provider.getAccount(),
151
+ });
152
+
153
+ return {
154
+ order,
155
+ preparedData: {
156
+ nonce: nonce.toString(),
157
+ signature: signature.slice(2),
158
+ },
159
+ };
160
+ }
161
+
162
+ static getDcaSingleChainPermissionMessage(
163
+ order: DcaSingleChainOrder,
164
+ nonce: bigint,
165
+ ): DcaSingleChainPermitTransferFrom {
166
+ const spender = DCA_SINGLE_CHAIN_GUARD_ADDRESSES[order.chainId as SupportedEvmChain] as Address;
167
+
168
+ const requestedOutput: TransferData = {
169
+ token: order.tokenOut as Address,
170
+ receiver: order.destinationAddress as Address,
171
+ amount: BigInt(order.amountOutMin),
172
+ };
173
+
174
+ const extraTransfers: TransferData[] =
175
+ order.extraTransfers?.map((transfer) => ({
176
+ token: transfer.token as Address,
177
+ receiver: transfer.receiver as Address,
178
+ amount: transfer.amount,
179
+ })) || [];
180
+
181
+ const totalAmountIn = BigInt(order.amountInPerInterval) * BigInt(order.totalIntervals);
182
+
183
+ return {
184
+ permitted: {
185
+ token: order.tokenIn as Address,
186
+ amount: totalAmountIn,
187
+ },
188
+ spender,
189
+ nonce,
190
+ deadline: BigInt(order.deadline),
191
+ witness: {
192
+ user: order.user as Address,
193
+ tokenIn: order.tokenIn as Address,
194
+ startTime: order.startTime,
195
+ amountInPerInterval: BigInt(order.amountInPerInterval),
196
+ totalIntervals: order.totalIntervals,
197
+ intervalDuration: order.intervalDuration,
198
+ requestedOutput,
199
+ encodedExternalCallData: '0x', // TODO: Update when external call data is supported
200
+ extraTransfers,
201
+ deadline: order.deadline,
202
+ nonce,
203
+ },
204
+ };
205
+ }
122
206
  }
@@ -2,7 +2,13 @@ import type { TypedDataDefinition } from 'viem';
2
2
  import type { SupportedEvmChain } from '../../chains.js';
3
3
  import type { CrossChainOrder } from '../orders/cross-chain.js';
4
4
  import { EVMIntentProvider } from './intent-provider.js';
5
- import { CROSS_CHAIN_PERMIT2_TYPES, PERMIT2_DOMAIN, SINGLE_CHAIN_LIMIT_PERMIT2_TYPES } from './permit2.js';
5
+ import type { DcaSingleChainOrder } from '../orders/dca-single-chain.js';
6
+ import {
7
+ CROSS_CHAIN_PERMIT2_TYPES,
8
+ DCA_SINGLE_CHAIN_PERMIT2_TYPES,
9
+ PERMIT2_DOMAIN,
10
+ SINGLE_CHAIN_LIMIT_PERMIT2_TYPES,
11
+ } from './permit2.js';
6
12
  import type { SingleChainOrder } from '../orders/single-chain.js';
7
13
 
8
14
  export async function getEVMSingleChainOrderTypedData(
@@ -41,3 +47,19 @@ export async function getEVMCrossChainOrderTypedData(
41
47
 
42
48
  return { orderTypedData, nonce: randomNonce };
43
49
  }
50
+
51
+ export async function getEVMDcaSingleChainOrderTypedData(
52
+ order: DcaSingleChainOrder,
53
+ ): Promise<{ orderTypedData: TypedDataDefinition; nonce: bigint }> {
54
+ const randomNonce = EVMIntentProvider.getRandomNonce();
55
+ const permissionMessage = EVMIntentProvider.getDcaSingleChainPermissionMessage(order, randomNonce);
56
+
57
+ const orderTypedData = {
58
+ domain: PERMIT2_DOMAIN(order.chainId as SupportedEvmChain),
59
+ types: DCA_SINGLE_CHAIN_PERMIT2_TYPES,
60
+ primaryType: 'PermitWitnessTransferFrom',
61
+ message: permissionMessage,
62
+ };
63
+
64
+ return { orderTypedData, nonce: randomNonce };
65
+ }
@@ -100,7 +100,6 @@ export const CROSS_CHAIN_PERMIT2_TYPES = {
100
100
  ],
101
101
  } as const;
102
102
 
103
- // Single-chain limit order types
104
103
  export const SINGLE_CHAIN_LIMIT_PERMIT2_TYPES = {
105
104
  TokenPermissions: TOKEN_PERMISSIONS_TYPE,
106
105
  TransferData: TRANSFER_DATA_TYPE,
@@ -128,3 +127,50 @@ export const PERMIT2_DOMAIN = (chainId: SupportedEvmChain): TypedDataDomain => (
128
127
  chainId,
129
128
  verifyingContract: PERMIT2_ADDRESS[chainId],
130
129
  });
130
+
131
+ type DcaSingleChainOrderWitness = {
132
+ user: Address;
133
+ tokenIn: Address;
134
+ startTime: number;
135
+ amountInPerInterval: bigint;
136
+ totalIntervals: number;
137
+ intervalDuration: number;
138
+ requestedOutput: TransferData;
139
+ extraTransfers: TransferData[];
140
+ encodedExternalCallData: Hex;
141
+ deadline: number;
142
+ nonce: bigint;
143
+ };
144
+
145
+ export type DcaSingleChainPermitTransferFrom = {
146
+ permitted: TokenPermissions;
147
+ spender: Address;
148
+ nonce: bigint;
149
+ deadline: bigint;
150
+ witness: DcaSingleChainOrderWitness;
151
+ };
152
+
153
+ export const DCA_SINGLE_CHAIN_PERMIT2_TYPES = {
154
+ TokenPermissions: TOKEN_PERMISSIONS_TYPE,
155
+ TransferData: TRANSFER_DATA_TYPE,
156
+ SingleChainDcaOrder: [
157
+ { type: 'address', name: 'user' },
158
+ { type: 'address', name: 'tokenIn' },
159
+ { type: 'uint32', name: 'startTime' },
160
+ { type: 'uint256', name: 'amountInPerInterval' },
161
+ { type: 'uint24', name: 'totalIntervals' },
162
+ { type: 'uint32', name: 'intervalDuration' },
163
+ { type: 'TransferData', name: 'requestedOutput' },
164
+ { type: 'TransferData[]', name: 'extraTransfers' },
165
+ { type: 'bytes', name: 'encodedExternalCallData' },
166
+ { type: 'uint32', name: 'deadline' },
167
+ { type: 'uint256', name: 'nonce' },
168
+ ],
169
+ PermitWitnessTransferFrom: [
170
+ { name: 'permitted', type: 'TokenPermissions' },
171
+ { name: 'spender', type: 'address' },
172
+ { name: 'nonce', type: 'uint256' },
173
+ { name: 'deadline', type: 'uint256' },
174
+ { name: 'witness', type: 'SingleChainDcaOrder' },
175
+ ],
176
+ } as const;
@@ -12,10 +12,13 @@ import {
12
12
  import type { ExtraTransfer } from '../orders/common.js';
13
13
  import type { CrossChainOrder } from '../orders/cross-chain.js';
14
14
  import type { SingleChainOrder } from '../orders/single-chain.js';
15
- import type { CrossChainOrderPrepared, SingleChainOrderPrepared } from '../../types/intent.js';
16
- import type { ApiUserOrders } from '../../types/api.js';
15
+ import type {
16
+ CrossChainOrderPrepared,
17
+ DcaSingleChainOrderPrepared,
18
+ SingleChainOrderPrepared,
19
+ } from '../../types/intent.js';
17
20
  import { fetchJWTToken, fetchSiweMessage } from '../../auth/siwe.js';
18
- import { fetchUserOrders } from '../orders/api/fetch.js';
21
+ import type { DcaSingleChainOrder } from '../orders/dca-single-chain.js';
19
22
 
20
23
  type CancelSingleChainOrderParams = {
21
24
  orderId: string;
@@ -47,7 +50,6 @@ type CancelCrossChainOrderParams = {
47
50
  export class EVMSDK extends BaseSDK {
48
51
  private readonly config: EVMConfig;
49
52
  private readonly evmIntentProvider: EVMIntentProvider;
50
- private token?: string;
51
53
 
52
54
  constructor(config: EVMConfig) {
53
55
  super();
@@ -187,22 +189,6 @@ export class EVMSDK extends BaseSDK {
187
189
  return newToken;
188
190
  }
189
191
 
190
- public setToken(token: string) {
191
- this.token = token;
192
-
193
- return this;
194
- }
195
-
196
- public override async getOrders(): Promise<ApiUserOrders> {
197
- if (!this.token) {
198
- throw new Error('No token provided');
199
- }
200
-
201
- const orders = await fetchUserOrders(this.token);
202
-
203
- return orders;
204
- }
205
-
206
192
  /**
207
193
  * Gets the user's Ethereum address derived from their private key
208
194
  * @returns Promise resolving to the user's Ethereum address as a 0x-prefixed string
@@ -240,8 +226,16 @@ export class EVMSDK extends BaseSDK {
240
226
  return this.evmIntentProvider.prepareCrossChainOrder(order);
241
227
  }
242
228
 
243
- protected async prepareSingleChainOrder(order: SingleChainOrder): Promise<SingleChainOrderPrepared> {
229
+ protected override async prepareSingleChainOrder(order: SingleChainOrder): Promise<SingleChainOrderPrepared> {
244
230
  await this.approveAllowanceIfNeeded(order.tokenIn as Address, order.amountIn);
245
231
  return this.evmIntentProvider.prepareSingleChainOrder(order);
246
232
  }
233
+
234
+ protected override async prepareDcaSingleChainOrder(
235
+ order: DcaSingleChainOrder,
236
+ ): Promise<DcaSingleChainOrderPrepared> {
237
+ const totalAmountIn = order.getTotalAmountIn();
238
+ await this.approveAllowanceIfNeeded(order.tokenIn as Address, totalAmountIn);
239
+ return this.evmIntentProvider.prepareDcaSingleChainOrder(order);
240
+ }
247
241
  }
@@ -7,6 +7,7 @@ import type { CreateCrossChainOrderParams } from '../orders/cross-chain.js';
7
7
  import { ERC20ABI } from './abi/erc20.js';
8
8
  import { ChainProvider } from './chain-provider.js';
9
9
  import type { CreateSingleChainOrderParams } from '../orders/single-chain.js';
10
+ import type { CreateDcaSingleChainOrderParams } from '../orders/dca-single-chain.js';
10
11
 
11
12
  /**
12
13
  * Chain-specific validator implementations
@@ -119,4 +120,35 @@ export class EvmValidator extends BaseValidator {
119
120
  // );
120
121
  // }
121
122
  }
123
+
124
+ override async validateDcaSingleChainOrderFeasability(
125
+ order: CreateDcaSingleChainOrderParams & { user: string },
126
+ ): Promise<void> {
127
+ const chain = order.chainId as SupportedEvmChain;
128
+
129
+ const client = ChainProvider.getClient(chain);
130
+ const user = order.user as Address;
131
+
132
+ if (isNativeEvmToken(order.tokenIn)) {
133
+ throw new ValidationError('Native token is not supported for DCA orders.');
134
+ }
135
+
136
+ const ERC20Contract = getContract({
137
+ address: order.tokenIn as Address,
138
+ abi: ERC20ABI,
139
+ client: {
140
+ public: client,
141
+ chain,
142
+ },
143
+ });
144
+
145
+ const totalAmountIn = order.amountInPerInterval * BigInt(order.totalIntervals);
146
+ const balance = await ERC20Contract.read.balanceOf([user]);
147
+
148
+ if (balance < totalAmountIn) {
149
+ throw new InsufficientBalanceError(
150
+ `Insufficient balance for DCA order. Token: ${order.tokenIn}. Current balance: ${balance}. Required total: ${totalAmountIn}`,
151
+ );
152
+ }
153
+ }
122
154
  }
@@ -0,0 +1,33 @@
1
+ import { AUCTIONEER_URL } from '../../../constants.js';
2
+ import type { ApiResponse, ApiUserOrders } from '../../../types/api.js';
3
+
4
+ export class AuctioneerAPI {
5
+ public aggregatedToken: string;
6
+
7
+ constructor(aggregatedToken: string) {
8
+ this.aggregatedToken = aggregatedToken;
9
+ }
10
+
11
+ public extend(token: string) {
12
+ this.aggregatedToken = token;
13
+ }
14
+
15
+ public async getUserOrders() {
16
+ const url = `${AUCTIONEER_URL}/user_intent`;
17
+ const response = await fetch(url, {
18
+ method: 'GET',
19
+ headers: {
20
+ 'Content-Type': 'application/json',
21
+ Authorization: `Bearer ${this.aggregatedToken}`,
22
+ },
23
+ });
24
+
25
+ const data: ApiResponse<ApiUserOrders> = await response.json();
26
+
27
+ if (!data.success) {
28
+ throw new Error(`Failed to fetch user orders: ${data.error}`);
29
+ }
30
+
31
+ return data.data!;
32
+ }
33
+ }
@@ -112,6 +112,10 @@ export class CrossChainOrder {
112
112
  user: input.user,
113
113
  });
114
114
 
115
+ if (isEvmChain(order.sourceChainId)) {
116
+ return order;
117
+ }
118
+
115
119
  const randomPreparedData = order.getRandomPreparedData();
116
120
  const intentRequest = order.toIntentRequest(randomPreparedData);
117
121
  await BaseSDK.validateCrossChainOrder(intentRequest);
@@ -0,0 +1,143 @@
1
+ // src/core/orders/dca-single-chain.ts
2
+ import { ChainID, chainIdToChainTypeMap, isEvmChain, type SupportedChain } from '../../chains.js';
3
+ import { ValidationError } from '../../errors/index.js';
4
+ import type { ApiResponse } from '../../types/api.js';
5
+ import type { DcaSingleChainPreparedData, DcaSingleChainUserIntentRequest } from '../../types/intent.js';
6
+ import { DcaSingleChainOrderValidator } from '../../utils/order-validator.js';
7
+ import { BaseSDK } from '../sdk.js';
8
+ import { getEVMDcaSingleChainOrderTypedData } from '../evm/order-signature.js';
9
+ import { type ExtraTransfer } from './common.js';
10
+ import { getSolanaDcaSingleChainOrderInstructions } from '../solana/dca/create-order.js';
11
+
12
+ export type CreateDcaSingleChainOrderParams = {
13
+ chainId: SupportedChain;
14
+ tokenIn: string;
15
+ tokenOut: string;
16
+ destinationAddress: string;
17
+ startTime: number;
18
+ amountInPerInterval: bigint;
19
+ totalIntervals: number;
20
+ intervalDuration: number;
21
+ amountOutMin?: bigint;
22
+ extraTransfers?: ExtraTransfer[];
23
+ deadline: number;
24
+ };
25
+
26
+ export class DcaSingleChainOrder {
27
+ public user: string;
28
+ public chainId: SupportedChain;
29
+ public tokenIn: string;
30
+ public tokenOut: string;
31
+ public destinationAddress: string;
32
+ public startTime: number;
33
+ public amountInPerInterval: bigint | number;
34
+ public totalIntervals: number;
35
+ public intervalDuration: number;
36
+ public amountOutMin: bigint | number;
37
+ public extraTransfers?: ExtraTransfer[];
38
+ public deadline: number;
39
+
40
+ private constructor(params: CreateDcaSingleChainOrderParams & { user: string }) {
41
+ this.user = params.user;
42
+ this.chainId = params.chainId;
43
+ this.tokenIn = params.tokenIn;
44
+ this.tokenOut = params.tokenOut;
45
+ this.destinationAddress = params.destinationAddress;
46
+ this.startTime = params.startTime;
47
+ this.amountInPerInterval = params.amountInPerInterval;
48
+ this.totalIntervals = params.totalIntervals;
49
+ this.intervalDuration = params.intervalDuration;
50
+ this.amountOutMin = params.amountOutMin ?? 1n;
51
+ this.extraTransfers = params.extraTransfers;
52
+ this.deadline = params.deadline;
53
+ }
54
+
55
+ public static async create(input: CreateDcaSingleChainOrderParams & { user: string }): Promise<DcaSingleChainOrder> {
56
+ new DcaSingleChainOrderValidator().validateOrder(input);
57
+
58
+ const order = new DcaSingleChainOrder({
59
+ ...input,
60
+ user: input.user,
61
+ });
62
+
63
+ if (isEvmChain(order.chainId)) {
64
+ return order;
65
+ }
66
+
67
+ const preparedRandomData = order.getRandomPreparedData();
68
+ const intentRequest = order.toIntentRequest(preparedRandomData);
69
+ await BaseSDK.validateDcaSingleChainOrder(intentRequest);
70
+
71
+ return order;
72
+ }
73
+
74
+ /// This is needed because API requires the prepared data to be sent
75
+ /// In the cases of Solana and Sui, if we want the real data, we must send the order on-chain before validating.
76
+ /// And that is something we cannot do before validating the order on the API side.
77
+ private getRandomPreparedData(): DcaSingleChainPreparedData {
78
+ const chainId = this.chainId;
79
+ const chainType = chainIdToChainTypeMap[chainId];
80
+
81
+ const randomNumber = String(Math.floor(Math.random() * 10000000));
82
+
83
+ switch (chainType) {
84
+ case 'EVM': {
85
+ return {
86
+ nonce: randomNumber,
87
+ signature: '0x0000000000000000000000000000000000000000000000000000000000000000',
88
+ };
89
+ }
90
+ case 'Solana': {
91
+ return {
92
+ secretNumber: randomNumber,
93
+ orderPubkey: 'DFNAjFAvS4GF98Tp1kiyLvEHM3wjGXibCfF86nnmhuVc',
94
+ };
95
+ }
96
+ case 'Sui': {
97
+ throw new ValidationError('DCA orders are not supported on Sui');
98
+ }
99
+
100
+ default: {
101
+ throw new Error('Chain type not supported');
102
+ }
103
+ }
104
+ }
105
+
106
+ public getTotalAmountIn(): bigint {
107
+ return BigInt(this.amountInPerInterval) * BigInt(this.totalIntervals);
108
+ }
109
+
110
+ public toIntentRequest(preparedData: DcaSingleChainPreparedData): DcaSingleChainUserIntentRequest {
111
+ const sourceChainType = chainIdToChainTypeMap[this.chainId];
112
+
113
+ return {
114
+ genericData: this,
115
+ chainSpecificData: {
116
+ [sourceChainType]: preparedData,
117
+ },
118
+ };
119
+ }
120
+
121
+ public sendToAuctioneer(preparedData: DcaSingleChainPreparedData): Promise<ApiResponse> {
122
+ return BaseSDK.sendDcaSingleChainOrder({
123
+ order: this,
124
+ preparedData,
125
+ });
126
+ }
127
+
128
+ public async toEVMTypedData() {
129
+ if (!isEvmChain(this.chainId)) {
130
+ throw new ValidationError('Chain id is not an Ethereum compatible chain');
131
+ }
132
+
133
+ return getEVMDcaSingleChainOrderTypedData(this);
134
+ }
135
+
136
+ public async toSolanaInstructionsByteArray() {
137
+ if (this.chainId !== ChainID.Solana) {
138
+ throw new ValidationError('Chain id is not Solana');
139
+ }
140
+
141
+ return getSolanaDcaSingleChainOrderInstructions(this);
142
+ }
143
+ }