@mimicprotocol/lib-ts 0.0.1-rc.11 → 0.0.1-rc.13

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/README.md CHANGED
@@ -53,9 +53,9 @@ $ yarn
53
53
  Here’s an example of how to use the library in a Mimic task:
54
54
 
55
55
  ```ts
56
- import { environment, Token } from '@mimicprotocol/lib-ts'
56
+ import { environment, ERC20Token } from '@mimicprotocol/lib-ts'
57
57
 
58
- const USDC = Token.fromString('0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', 1)
58
+ const USDC = ERC20Token.fromString('0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', 1)
59
59
 
60
60
  environment.getPrice(USDC, new Date(1744818017000))
61
61
  ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mimicprotocol/lib-ts",
3
- "version": "0.0.1-rc.11",
3
+ "version": "0.0.1-rc.13",
4
4
  "license": "GPL-3.0",
5
5
  "private": false,
6
6
  "type": "module",
@@ -19,8 +19,9 @@
19
19
  "assemblyscript": "0.27.36"
20
20
  },
21
21
  "dependencies": {
22
+ "as-base58": "^0.1.1",
22
23
  "eslint-config-mimic": "^0.0.3",
23
24
  "json-as": "1.1.7",
24
25
  "visitor-as": "0.11.4"
25
26
  }
26
- }
27
+ }
@@ -1,14 +1,14 @@
1
- import { Token } from '../tokens'
1
+ import { ERC20Token } from '../tokens/ERC20Token'
2
2
  import { ChainId } from '../types'
3
3
 
4
4
  /* eslint-disable no-secrets/no-secrets */
5
5
 
6
6
  export namespace Arbitrum {
7
7
  export const CHAIN_ID = ChainId.ARBITRUM
8
- export const ETH = Token.native(CHAIN_ID)
9
- export const USDC = Token.fromString('0xaf88d065e77c8cC2239327C5EDb3A432268e5831', CHAIN_ID, 6, 'USDC')
10
- export const USDT = Token.fromString('0xfd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9', CHAIN_ID, 6, 'USDT')
11
- export const DAI = Token.fromString('0xda10009cbd5d07dd0cecc66161fc93d7c9000da1', CHAIN_ID, 18, 'DAI')
12
- export const WBTC = Token.fromString('0x2f2a2543b76a4166549f7aab2e75bef0aefc5b0f', CHAIN_ID, 8, 'WBTC')
13
- export const WETH = Token.fromString('0x82af49447d8a07e3bd95bd0d56f35241523fbab1', CHAIN_ID, 18, 'WETH')
8
+ export const ETH = ERC20Token.native(CHAIN_ID)
9
+ export const USDC = ERC20Token.fromString('0xaf88d065e77c8cC2239327C5EDb3A432268e5831', CHAIN_ID, 6, 'USDC')
10
+ export const USDT = ERC20Token.fromString('0xfd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9', CHAIN_ID, 6, 'USDT')
11
+ export const DAI = ERC20Token.fromString('0xda10009cbd5d07dd0cecc66161fc93d7c9000da1', CHAIN_ID, 18, 'DAI')
12
+ export const WBTC = ERC20Token.fromString('0x2f2a2543b76a4166549f7aab2e75bef0aefc5b0f', CHAIN_ID, 8, 'WBTC')
13
+ export const WETH = ERC20Token.fromString('0x82af49447d8a07e3bd95bd0d56f35241523fbab1', CHAIN_ID, 18, 'WETH')
14
14
  }
@@ -1,14 +1,14 @@
1
- import { Token } from '../tokens'
1
+ import { ERC20Token } from '../tokens'
2
2
  import { ChainId } from '../types'
3
3
 
4
4
  /* eslint-disable no-secrets/no-secrets */
5
5
 
6
6
  export namespace Base {
7
7
  export const CHAIN_ID = ChainId.BASE
8
- export const ETH = Token.native(CHAIN_ID)
9
- export const USDC = Token.fromString('0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', CHAIN_ID, 6, 'USDC')
10
- export const USDT = Token.fromString('0xfde4c96c8593536e31f229ea8f37b2ada2699bb2', CHAIN_ID, 6, 'USDT')
11
- export const DAI = Token.fromString('0x50c5725949a6f0c72e6c4a641f24049a917db0cb', CHAIN_ID, 18, 'DAI')
12
- export const WBTC = Token.fromString('0x0555e30da8f98308edb960aa94c0db47230d2b9c', CHAIN_ID, 8, 'WBTC')
13
- export const WETH = Token.fromString('0x4200000000000000000000000000000000000006', CHAIN_ID, 18, 'WETH')
8
+ export const ETH = ERC20Token.native(CHAIN_ID)
9
+ export const USDC = ERC20Token.fromString('0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', CHAIN_ID, 6, 'USDC')
10
+ export const USDT = ERC20Token.fromString('0xfde4c96c8593536e31f229ea8f37b2ada2699bb2', CHAIN_ID, 6, 'USDT')
11
+ export const DAI = ERC20Token.fromString('0x50c5725949a6f0c72e6c4a641f24049a917db0cb', CHAIN_ID, 18, 'DAI')
12
+ export const WBTC = ERC20Token.fromString('0x0555e30da8f98308edb960aa94c0db47230d2b9c', CHAIN_ID, 8, 'WBTC')
13
+ export const WETH = ERC20Token.fromString('0x4200000000000000000000000000000000000006', CHAIN_ID, 18, 'WETH')
14
14
  }
@@ -1,12 +1,12 @@
1
- import { Token } from '../tokens'
1
+ import { ERC20Token } from '../tokens'
2
2
  import { ChainId } from '../types'
3
3
 
4
4
  export namespace Ethereum {
5
5
  export const CHAIN_ID = ChainId.ETHEREUM
6
- export const ETH = Token.native(CHAIN_ID)
7
- export const USDC = Token.fromString('0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', CHAIN_ID, 6, 'USDC')
8
- export const USDT = Token.fromString('0xdac17f958d2ee523a2206206994597c13d831ec7', CHAIN_ID, 6, 'USDT')
9
- export const DAI = Token.fromString('0x6b175474e89094c44da98b954eedeac495271d0f', CHAIN_ID, 18, 'DAI')
10
- export const WBTC = Token.fromString('0x2260fac5e5542a773aa44fbcfedf7c193bc2c599', CHAIN_ID, 8, 'WBTC')
11
- export const WETH = Token.fromString('0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', CHAIN_ID, 18, 'WETH')
6
+ export const ETH = ERC20Token.native(CHAIN_ID)
7
+ export const USDC = ERC20Token.fromString('0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', CHAIN_ID, 6, 'USDC')
8
+ export const USDT = ERC20Token.fromString('0xdac17f958d2ee523a2206206994597c13d831ec7', CHAIN_ID, 6, 'USDT')
9
+ export const DAI = ERC20Token.fromString('0x6b175474e89094c44da98b954eedeac495271d0f', CHAIN_ID, 18, 'DAI')
10
+ export const WBTC = ERC20Token.fromString('0x2260fac5e5542a773aa44fbcfedf7c193bc2c599', CHAIN_ID, 8, 'WBTC')
11
+ export const WETH = ERC20Token.fromString('0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', CHAIN_ID, 18, 'WETH')
12
12
  }
@@ -1,14 +1,14 @@
1
- import { Token } from '../tokens'
1
+ import { ERC20Token } from '../tokens'
2
2
  import { ChainId } from '../types'
3
3
 
4
4
  /* eslint-disable no-secrets/no-secrets */
5
5
 
6
6
  export namespace Gnosis {
7
7
  export const CHAIN_ID = ChainId.GNOSIS
8
- export const xDAI = Token.native(CHAIN_ID)
9
- export const USDC = Token.fromString('0x2a22f9c3b484c3629090feed35f17ff8f88f76f0', CHAIN_ID, 6, 'USDC')
10
- export const USDT = Token.fromString('0x4ecaba5870353805a9f068101a40e0f32ed605c6', CHAIN_ID, 6, 'USDT')
11
- export const WXDAI = Token.fromString('0xe91D153E0b41518A2Ce8Dd3D7944Fa863463a97d', CHAIN_ID, 18, 'WXDAI')
12
- export const WBTC = Token.fromString('0x8e5bbbb09ed1ebde8674cda39a0c169401db4252', CHAIN_ID, 8, 'WBTC')
13
- export const WETH = Token.fromString('0x6a023ccd1ff6f2045c3309768ead9e68f978f6e1', CHAIN_ID, 18, 'WETH')
8
+ export const xDAI = ERC20Token.native(CHAIN_ID)
9
+ export const USDC = ERC20Token.fromString('0x2a22f9c3b484c3629090feed35f17ff8f88f76f0', CHAIN_ID, 6, 'USDC')
10
+ export const USDT = ERC20Token.fromString('0x4ecaba5870353805a9f068101a40e0f32ed605c6', CHAIN_ID, 6, 'USDT')
11
+ export const WXDAI = ERC20Token.fromString('0xe91D153E0b41518A2Ce8Dd3D7944Fa863463a97d', CHAIN_ID, 18, 'WXDAI')
12
+ export const WBTC = ERC20Token.fromString('0x8e5bbbb09ed1ebde8674cda39a0c169401db4252', CHAIN_ID, 8, 'WBTC')
13
+ export const WETH = ERC20Token.fromString('0x6a023ccd1ff6f2045c3309768ead9e68f978f6e1', CHAIN_ID, 18, 'WETH')
14
14
  }
@@ -1,12 +1,12 @@
1
- import { Token } from '../tokens'
1
+ import { ERC20Token } from '../tokens'
2
2
  import { ChainId } from '../types'
3
3
 
4
4
  export namespace Optimism {
5
5
  export const CHAIN_ID = ChainId.OPTIMISM
6
- export const ETH = Token.native(CHAIN_ID)
7
- export const USDC = Token.fromString('0x0b2c639c533813f4aa9d7837caf62653d097ff85', CHAIN_ID, 6, 'USDC')
8
- export const USDT = Token.fromString('0x94b008aa00579c1307b0ef2c499ad98a8ce58e58', CHAIN_ID, 6, 'USDT')
9
- export const DAI = Token.fromString('0xda10009cbd5d07dd0cecc66161fc93d7c9000da1', CHAIN_ID, 18, 'DAI')
10
- export const WBTC = Token.fromString('0x68f180fcce6836688e9084f035309e29bf0a2095', CHAIN_ID, 8, 'WBTC')
11
- export const WETH = Token.fromString('0x4200000000000000000000000000000000000006', CHAIN_ID, 18, 'WETH')
6
+ export const ETH = ERC20Token.native(CHAIN_ID)
7
+ export const USDC = ERC20Token.fromString('0x0b2c639c533813f4aa9d7837caf62653d097ff85', CHAIN_ID, 6, 'USDC')
8
+ export const USDT = ERC20Token.fromString('0x94b008aa00579c1307b0ef2c499ad98a8ce58e58', CHAIN_ID, 6, 'USDT')
9
+ export const DAI = ERC20Token.fromString('0xda10009cbd5d07dd0cecc66161fc93d7c9000da1', CHAIN_ID, 18, 'DAI')
10
+ export const WBTC = ERC20Token.fromString('0x68f180fcce6836688e9084f035309e29bf0a2095', CHAIN_ID, 8, 'WBTC')
11
+ export const WETH = ERC20Token.fromString('0x4200000000000000000000000000000000000006', CHAIN_ID, 18, 'WETH')
12
12
  }
@@ -51,7 +51,9 @@ export class Context {
51
51
 
52
52
  findSettler(chainId: ChainId): Address {
53
53
  for (let i = 0; i < this.settlers.length; i++) {
54
- if (this.settlers[i].chainId === chainId) return this.settlers[i].address
54
+ if (this.settlers[i].chainId === chainId) {
55
+ return this.settlers[i].address
56
+ }
55
57
  }
56
58
  throw new Error(`Settler not found for chainId: ${chainId}`)
57
59
  }
@@ -1,10 +1,11 @@
1
+ import { JSON } from 'json-as/assembly'
2
+
3
+ import { Context, SerializableContext } from './context'
1
4
  import { ListType } from './helpers'
2
- import { Token, TokenAmount, USD } from './tokens'
3
- import { Address, BigInt, ChainId } from './types'
4
5
  import { Swap, Transfer, Call } from './intents'
5
6
  import { Call as CallQuery, GetPrice, GetRelevantTokens, GetRelevantTokensResponse } from './queries'
6
- import { JSON } from 'json-as/assembly'
7
- import { Context, SerializableContext } from './context'
7
+ import { ERC20Token, Token, TokenAmount, USD } from './tokens'
8
+ import { Address, BigInt, ChainId } from './types'
8
9
 
9
10
  export namespace environment {
10
11
  @external('environment', '_call')
@@ -59,7 +60,9 @@ export namespace environment {
59
60
  * @returns The token price in USD
60
61
  */
61
62
  export function getPrice(token: Token, timestamp: Date | null = null): USD {
62
- const price = _getPrice(JSON.stringify(GetPrice.fromToken(token, timestamp)))
63
+ if (token.isUSD()) return USD.fromI32(1)
64
+ else if (!token instanceof ERC20Token) throw new Error('Price query not supported for token ' + token.toString())
65
+ const price = _getPrice(JSON.stringify(GetPrice.fromERC20Token(token as ERC20Token, timestamp)))
63
66
  return USD.fromBigInt(BigInt.fromString(price))
64
67
  }
65
68
 
@@ -68,15 +71,16 @@ export namespace environment {
68
71
  * @param address - The address to query balances for
69
72
  * @param chainIds - Array of chain ids to search
70
73
  * @param usdMinAmount - Minimum USD value threshold for tokens (optional, defaults to zero)
71
- * @param tokensList - List of tokens to include/exclude (optional, defaults to empty array)
74
+ * @param tokensList - List of ERC20 tokens to include/exclude (optional, defaults to empty array)
72
75
  * @param listType - Whether to include (AllowList) or exclude (DenyList) the tokens in `tokensList` (optional, defaults to DenyList)
76
+ * @param timestamp - The timestamp for relevant tokens qery (optional, defaults to current time)
73
77
  * @returns Array of TokenAmount objects representing the relevant tokens
74
78
  */
75
79
  export function getRelevantTokens(
76
80
  address: Address,
77
81
  chainIds: ChainId[],
78
82
  usdMinAmount: USD = USD.zero(),
79
- tokensList: Token[] = [],
83
+ tokensList: ERC20Token[] = [],
80
84
  listType: ListType = ListType.DenyList,
81
85
  timestamp: Date | null = null
82
86
  ): TokenAmount[] {
@@ -1,5 +1,6 @@
1
1
  export const NULL_ADDRESS = '0x0000000000000000000000000000000000000000'
2
2
  export const NATIVE_ADDRESS = '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee'
3
+ export const USD_ADDRESS = '0x0000000000000000000000000000000000000348'
3
4
 
4
5
  export const STANDARD_DECIMALS: u8 = 18
5
6
 
@@ -1,3 +1,7 @@
1
+ import { decode, encode } from 'as-base58/assembly/index'
2
+
3
+ import { ByteArray } from '../types'
4
+
1
5
  export function bytesToString(bytes: Uint8Array): string {
2
6
  return String.UTF8.decodeUnsafe(bytes.dataStart, bytes.length)
3
7
  }
@@ -8,6 +12,15 @@ export function bytesToHexString(bytes: Uint8Array): string {
8
12
  return hex
9
13
  }
10
14
 
15
+ export function bytesToBase58String(bytes: Uint8Array): string {
16
+ return encode(bytes)
17
+ }
18
+
19
+ export function bytesFromBase58String(base58: string): ByteArray {
20
+ assert(isBase58(base58), `input ${base58} is not valid base58`)
21
+ return changetype<ByteArray>(decode(base58))
22
+ }
23
+
11
24
  export function areAllZeros(str: string): boolean {
12
25
  for (let i = 0; i < str.length; i++) if (str.charCodeAt(i) !== 48) return false
13
26
  return true
@@ -101,3 +114,21 @@ export function isHex(str: string, strict: boolean = false): boolean {
101
114
 
102
115
  return true
103
116
  }
117
+
118
+ export function isBase58(str: string): boolean {
119
+ for (let i = 0; i < str.length; i++) {
120
+ const c = str.charCodeAt(i)
121
+
122
+ // Base58 alphabet: digits, letters (not 0IOl)
123
+ if (
124
+ (c >= '1'.charCodeAt(0) && c <= '9'.charCodeAt(0)) ||
125
+ (c >= 'A'.charCodeAt(0) && c <= 'Z'.charCodeAt(0) && c !== 'I'.charCodeAt(0) && c !== 'O'.charCodeAt(0)) ||
126
+ (c >= 'a'.charCodeAt(0) && c <= 'z'.charCodeAt(0) && c !== 'l'.charCodeAt(0))
127
+ ) {
128
+ continue
129
+ }
130
+ return false
131
+ }
132
+
133
+ return true
134
+ }
@@ -12,7 +12,6 @@ import { Intent, IntentBuilder, OperationType } from './Intent'
12
12
  export class CallBuilder extends IntentBuilder {
13
13
  private chainId: ChainId
14
14
  private calls: CallData[] = []
15
- private fee: TokenAmount | null = null
16
15
 
17
16
  /**
18
17
  * Creates a CallBuilder for the specified blockchain network.
@@ -23,18 +22,6 @@ export class CallBuilder extends IntentBuilder {
23
22
  return new CallBuilder(chainId)
24
23
  }
25
24
 
26
- /**
27
- * Creates a CallBuilder with a pre-configured fee.
28
- * @param chainId - The blockchain network identifier
29
- * @param fee - The fee token amount to be charged for the call
30
- * @returns A new CallBuilder instance with fee already set
31
- */
32
- static forChainWithFee(chainId: ChainId, fee: TokenAmount): CallBuilder {
33
- const builder = new CallBuilder(chainId)
34
- builder.addFee(fee)
35
- return builder
36
- }
37
-
38
25
  /**
39
26
  * Creates a new CallBuilder instance.
40
27
  * @param chainId - The blockchain network identifier
@@ -56,17 +43,6 @@ export class CallBuilder extends IntentBuilder {
56
43
  return this
57
44
  }
58
45
 
59
- /**
60
- * Sets the fee to be charged for executing this call intent.
61
- * @param fee - The fee token amount (must be on the same chain as the call)
62
- * @returns This CallBuilder instance for method chaining
63
- */
64
- addFee(fee: TokenAmount): CallBuilder {
65
- if (fee.token.chainId !== this.chainId) throw new Error('Fee token must be on the same chain')
66
- this.fee = fee
67
- return this
68
- }
69
-
70
46
  /**
71
47
  * Sets the settler address for this intent.
72
48
  * @param settler - The settler address as an Address instance
@@ -121,21 +97,23 @@ export class CallBuilder extends IntentBuilder {
121
97
  return changetype<CallBuilder>(super.addNonce(nonce))
122
98
  }
123
99
 
100
+ /**
101
+ * Adds a max fee for this intent.
102
+ * @param fee - The max fee token amount (must be on same chain)
103
+ * @returns This CallBuilder instance for method chaining
104
+ */
105
+ addMaxFee(fee: TokenAmount): CallBuilder {
106
+ if (!fee.token.hasChain(this.chainId)) throw new Error('Fee token must be on the same chain')
107
+ this.maxFees.push(fee)
108
+ return this
109
+ }
110
+
124
111
  /**
125
112
  * Builds and returns the final Call intent.
126
113
  * @returns A new Call instance with all configured parameters
127
114
  */
128
115
  build(): Call {
129
- if (!this.fee) throw new Error('Call fee must be specified')
130
- return new Call(
131
- this.chainId,
132
- this.calls,
133
- this.fee as TokenAmount,
134
- this.settler,
135
- this.user,
136
- this.deadline,
137
- this.nonce
138
- )
116
+ return new Call(this.chainId, this.calls, this.maxFees, this.settler, this.user, this.deadline, this.nonce)
139
117
  }
140
118
  }
141
119
 
@@ -169,15 +147,13 @@ export class CallData {
169
147
  export class Call extends Intent {
170
148
  public chainId: ChainId
171
149
  public calls: CallData[]
172
- public feeToken: string
173
- public feeAmount: string
174
150
 
175
151
  /**
176
152
  * Creates a Call intent with a single contract call.
177
153
  * @param chainId - The blockchain network identifier
178
154
  * @param target - The contract address to call
179
155
  * @param data - The call data
180
- * @param fee - The fee token amount to be charged
156
+ * @param maxFee - The max fee to pay for the call intent
181
157
  * @param value - The native token value to send (optional, defaults to zero)
182
158
  * @param settler - The settler address (optional)
183
159
  * @param user - The user address (optional)
@@ -189,7 +165,7 @@ export class Call extends Intent {
189
165
  chainId: ChainId,
190
166
  target: Address,
191
167
  data: Bytes,
192
- fee: TokenAmount,
168
+ maxFee: TokenAmount,
193
169
  value: BigInt = BigInt.zero(),
194
170
  settler: Address | null = null,
195
171
  user: Address | null = null,
@@ -197,14 +173,14 @@ export class Call extends Intent {
197
173
  nonce: string | null = null
198
174
  ): Call {
199
175
  const callData = new CallData(target, data, value)
200
- return new Call(chainId, [callData], fee, settler, user, deadline, nonce)
176
+ return new Call(chainId, [callData], [maxFee], settler, user, deadline, nonce)
201
177
  }
202
178
 
203
179
  /**
204
180
  * Creates a new Call intent.
205
181
  * @param chainId - The blockchain network identifier
206
182
  * @param calls - Array of contract calls to execute
207
- * @param fee - The fee token amount to be charged
183
+ * @param maxFees - The list of max fees to pay for the call intent
208
184
  * @param settler - The settler address (optional)
209
185
  * @param user - The user address (optional)
210
186
  * @param deadline - The deadline timestamp (optional)
@@ -213,19 +189,17 @@ export class Call extends Intent {
213
189
  constructor(
214
190
  chainId: ChainId,
215
191
  calls: CallData[],
216
- fee: TokenAmount,
192
+ maxFees: TokenAmount[],
217
193
  settler: Address | null = null,
218
194
  user: Address | null = null,
219
195
  deadline: BigInt | null = null,
220
196
  nonce: string | null = null
221
197
  ) {
222
- super(OperationType.Call, chainId, settler, user, deadline, nonce)
223
-
198
+ super(OperationType.Call, chainId, maxFees, settler, user, deadline, nonce)
224
199
  if (calls.length === 0) throw new Error('Call list cannot be empty')
200
+ if (maxFees.length == 0) throw new Error('At least a max fee must be specified')
225
201
 
226
202
  this.calls = calls
227
- this.feeToken = fee.token.address.toString()
228
- this.feeAmount = fee.amount.toString()
229
203
  this.chainId = chainId
230
204
  }
231
205
 
@@ -1,6 +1,7 @@
1
1
  import { environment } from '../environment'
2
2
  import { evm } from '../evm'
3
3
  import { NULL_ADDRESS } from '../helpers'
4
+ import { TokenAmount } from '../tokens'
4
5
  import { Address, BigInt, ChainId } from '../types'
5
6
 
6
7
  export enum OperationType {
@@ -11,40 +12,85 @@ export enum OperationType {
11
12
 
12
13
  const DEFAULT_DEADLINE = 5 * 60 // 5 minutes in seconds
13
14
 
15
+ /**
16
+ * Base builder for creating intents.
17
+ */
14
18
  export abstract class IntentBuilder {
15
19
  protected user: Address | null = null
16
20
  protected settler: Address | null = null
17
21
  protected deadline: BigInt | null = null
18
22
  protected nonce: string | null = null
23
+ protected maxFees: TokenAmount[] = []
19
24
 
25
+ /**
26
+ * Sets the settler address for this intent.
27
+ * @param settler - The settler address as an Address instance
28
+ * @returns This IntentBuilder instance for method chaining
29
+ */
20
30
  addSettler(settler: Address): IntentBuilder {
21
31
  this.settler = settler
22
32
  return this
23
33
  }
24
34
 
35
+ /**
36
+ * Sets the settler address from a string.
37
+ * @param settler - The settler address as a hex string
38
+ * @returns This IntentBuilder instance for method chaining
39
+ */
25
40
  addSettlerAsString(settler: string): IntentBuilder {
26
41
  return this.addSettler(Address.fromString(settler))
27
42
  }
28
43
 
44
+ /**
45
+ * Sets the deadline for this intent.
46
+ * @param deadline - The deadline as a timestamp
47
+ * @returns This IntentBuilder instance for method chaining
48
+ */
29
49
  addDeadline(deadline: BigInt): IntentBuilder {
30
50
  this.deadline = deadline
31
51
  return this
32
52
  }
33
53
 
54
+ /**
55
+ * Sets the user address for this intent.
56
+ * @param user - The user address
57
+ * @returns This IntentBuilder instance for method chaining
58
+ */
34
59
  addUser(user: Address): IntentBuilder {
35
60
  this.user = user
36
61
  return this
37
62
  }
38
63
 
64
+ /**
65
+ * Sets the user address from a string.
66
+ * @param user - The user address as a hex string
67
+ * @returns This IntentBuilder instance for method chaining
68
+ */
39
69
  addUserAsString(user: string): IntentBuilder {
40
70
  return this.addUser(Address.fromString(user))
41
71
  }
42
72
 
73
+ /**
74
+ * Sets the nonce for this intent.
75
+ * @param nonce - The nonce to be set for the intent
76
+ * @returns This IntentBuilder instance for method chaining
77
+ */
43
78
  addNonce(nonce: string): IntentBuilder {
44
79
  this.nonce = nonce
45
80
  return this
46
81
  }
47
82
 
83
+ /**
84
+ * Adds a max fee for this intent.
85
+ * @param fee - The max fee token amount (must be on same chain)
86
+ * @returns This IntentBuilder instance for method chaining
87
+ */
88
+ abstract addMaxFee(fee: TokenAmount): IntentBuilder
89
+
90
+ /**
91
+ * Builds and returns the final intent.
92
+ * @returns A new intent
93
+ */
48
94
  abstract build(): Intent
49
95
  }
50
96
 
@@ -56,10 +102,23 @@ export abstract class Intent {
56
102
  public user: string
57
103
  public deadline: string
58
104
  public nonce: string
105
+ public maxFeeTokens: string[]
106
+ public maxFeeAmounts: string[]
59
107
 
108
+ /**
109
+ * Creates a new intent.
110
+ * @param op - The type of intent to be created
111
+ * @param chainId - The chain ID for fetch the settler
112
+ * @param maxFees - The list of max fees to pay for the intent
113
+ * @param settler - The settler address (optional)
114
+ * @param user - The user address (optional)
115
+ * @param deadline - The deadline timestamp (optional)
116
+ * @param nonce - The nonce for replay protection (optional)
117
+ */
60
118
  protected constructor(
61
119
  op: OperationType,
62
120
  chainId: ChainId,
121
+ maxFees: TokenAmount[],
63
122
  settler: Address | null,
64
123
  user: Address | null,
65
124
  deadline: BigInt | null,
@@ -71,10 +130,15 @@ export abstract class Intent {
71
130
  this.deadline = deadline ? deadline.toString() : (context.timestamp / 1000 + DEFAULT_DEADLINE).toString()
72
131
  this.user = user ? user.toString() : context.user.toString()
73
132
  this.nonce = nonce ? nonce : evm.keccak(`${context.configSig}${context.timestamp}${++INTENT_INDEX}`)
133
+ this.maxFeeTokens = maxFees.map((fee: TokenAmount) => fee.token.address.toString())
134
+ this.maxFeeAmounts = maxFees.map((fee: TokenAmount) => fee.amount.toString())
74
135
 
75
136
  if (!this.user || this.user == NULL_ADDRESS) throw new Error('A user must be specified')
76
137
  if (!this.settler || this.settler == NULL_ADDRESS) throw new Error('A settler contract must be specified')
77
138
  }
78
139
 
140
+ /**
141
+ * Sends this intent to the execution environment.
142
+ */
79
143
  abstract send(): void
80
144
  }
@@ -1,5 +1,5 @@
1
1
  import { environment } from '../environment'
2
- import { Token, TokenAmount } from '../tokens'
2
+ import { ERC20Token, Token, TokenAmount } from '../tokens'
3
3
  import { Address, BigInt, ChainId } from '../types'
4
4
 
5
5
  import { Intent, IntentBuilder, OperationType } from './Intent'
@@ -90,7 +90,7 @@ export class SwapBuilder extends IntentBuilder {
90
90
  * @returns This SwapBuilder instance for method chaining
91
91
  */
92
92
  addTokenInFromTokenAmount(tokenAmount: TokenAmount): SwapBuilder {
93
- if (tokenAmount.token.chainId !== this.sourceChain) throw new Error('Tokens in must be on the same chain')
93
+ if (!tokenAmount.token.hasChain(this.sourceChain)) throw new Error('Tokens in must be on the same chain')
94
94
  return this.addTokenIn(TokenIn.fromTokenAmount(tokenAmount))
95
95
  }
96
96
 
@@ -111,7 +111,7 @@ export class SwapBuilder extends IntentBuilder {
111
111
  * @returns This SwapBuilder instance for method chaining
112
112
  */
113
113
  addTokenInFromStringDecimal(token: Token, amount: string): SwapBuilder {
114
- if (token.chainId !== this.sourceChain) throw new Error('Tokens in must be on the same chain')
114
+ if (!token.hasChain(this.sourceChain)) throw new Error('Tokens in must be on the source chain')
115
115
  return this.addTokenIn(TokenIn.fromStringDecimal(token, amount))
116
116
  }
117
117
 
@@ -122,7 +122,8 @@ export class SwapBuilder extends IntentBuilder {
122
122
  * @returns This SwapBuilder instance for method chaining
123
123
  */
124
124
  addTokenOutFromTokenAmount(tokenAmount: TokenAmount, recipient: Address): SwapBuilder {
125
- if (tokenAmount.token.chainId !== this.destinationChain) throw new Error('Tokens out must be on the same chain')
125
+ if (!tokenAmount.token.hasChain(this.destinationChain))
126
+ throw new Error('Tokens out must be on the destination chain')
126
127
  return this.addTokenOut(TokenOut.fromTokenAmount(tokenAmount, recipient))
127
128
  }
128
129
 
@@ -145,7 +146,7 @@ export class SwapBuilder extends IntentBuilder {
145
146
  * @returns This SwapBuilder instance for method chaining
146
147
  */
147
148
  addTokenOutFromStringDecimal(token: Token, amount: string, recipient: Address): SwapBuilder {
148
- if (token.chainId !== this.destinationChain) throw new Error('Tokens out must be on the same chain')
149
+ if (!token.hasChain(this.destinationChain)) throw new Error('Tokens out must be on the destination chain')
149
150
  return this.addTokenOut(TokenOut.fromStringDecimal(token, amount, recipient))
150
151
  }
151
152
 
@@ -203,6 +204,17 @@ export class SwapBuilder extends IntentBuilder {
203
204
  return changetype<SwapBuilder>(super.addNonce(nonce))
204
205
  }
205
206
 
207
+ /**
208
+ * Adds a max fee for this intent.
209
+ * @param fee - The max fee token amount (must be on same chain)
210
+ * @returns This SwapBuilder instance for method chaining
211
+ */
212
+ addMaxFee(fee: TokenAmount): SwapBuilder {
213
+ if (!fee.token.hasChain(this.destinationChain)) throw new Error('Fee token must be on the destination chain')
214
+ this.maxFees.push(fee)
215
+ return this
216
+ }
217
+
206
218
  /**
207
219
  * Builds and returns the final Swap intent.
208
220
  * @returns A new Swap instance with all configured parameters
@@ -218,7 +230,8 @@ export class SwapBuilder extends IntentBuilder {
218
230
  this.settler,
219
231
  this.user,
220
232
  this.deadline,
221
- this.nonce
233
+ this.nonce,
234
+ this.maxFees
222
235
  )
223
236
  }
224
237
  }
@@ -379,8 +392,8 @@ export class Swap extends Intent {
379
392
  ): Swap {
380
393
  const context = environment.getContext()
381
394
  const recipient = user || context.user
382
- const swapIn = TokenIn.fromBigInt(Token.fromAddress(tokenIn, chainId), amountIn)
383
- const swapOut = TokenOut.fromBigInt(Token.fromAddress(tokenOut, chainId), minAmountOut, recipient)
395
+ const swapIn = TokenIn.fromBigInt(ERC20Token.fromAddress(tokenIn, chainId), amountIn)
396
+ const swapOut = TokenOut.fromBigInt(ERC20Token.fromAddress(tokenOut, chainId), minAmountOut, recipient)
384
397
  return new Swap(chainId, [swapIn], [swapOut], chainId, settler, user, deadline, nonce)
385
398
  }
386
399
 
@@ -394,6 +407,7 @@ export class Swap extends Intent {
394
407
  * @param user - The user address (optional)
395
408
  * @param deadline - The deadline timestamp (optional)
396
409
  * @param nonce - The nonce for replay protection (optional)
410
+ * @param maxFees - The list of max fees to pay for the swap intent (optional)
397
411
  */
398
412
  constructor(
399
413
  public sourceChain: ChainId,
@@ -403,9 +417,10 @@ export class Swap extends Intent {
403
417
  settler: Address | null = null,
404
418
  user: Address | null = null,
405
419
  deadline: BigInt | null = null,
406
- nonce: string | null = null
420
+ nonce: string | null = null,
421
+ maxFees: TokenAmount[] | null = null
407
422
  ) {
408
- super(OperationType.Swap, sourceChain, settler, user, deadline, nonce)
423
+ super(OperationType.Swap, sourceChain, maxFees || [], settler, user, deadline, nonce)
409
424
  if (tokensIn.length === 0) throw new Error('TokenIn list cannot be empty')
410
425
  if (tokensOut.length === 0) throw new Error('TokenOut list cannot be empty')
411
426
  }