@mimicprotocol/lib-ts 0.0.1-rc.9

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.
@@ -0,0 +1,349 @@
1
+ import { environment } from '../environment'
2
+ import { Token, TokenAmount } from '../tokens'
3
+ import { Address, BigInt, ChainId } from '../types'
4
+
5
+ import { Intent, IntentBuilder, OperationType } from './Intent'
6
+
7
+ /**
8
+ * Builder for creating Transfer intents with token transfer operations.
9
+ * Supports multiple transfers within a single transaction on the same chain.
10
+ */
11
+ export class TransferBuilder extends IntentBuilder {
12
+ private chainId: ChainId
13
+ private transfers: TransferData[] = []
14
+ private fee: TokenAmount | null = null
15
+
16
+ /**
17
+ * Creates a TransferBuilder for a specific chain.
18
+ * @param chainId - The blockchain network identifier
19
+ * @returns A new TransferBuilder instance
20
+ */
21
+ static forChain(chainId: ChainId): TransferBuilder {
22
+ return new TransferBuilder(chainId)
23
+ }
24
+
25
+ /**
26
+ * Creates a TransferBuilder for a specific chain with a pre-configured fee.
27
+ * @param chainId - The blockchain network identifier
28
+ * @param fee - The fee token amount for the transfer
29
+ * @returns A new TransferBuilder instance with fee set
30
+ */
31
+ static forChainWithFee(chainId: ChainId, fee: TokenAmount): TransferBuilder {
32
+ const builder = new TransferBuilder(chainId)
33
+ builder.addFee(fee)
34
+ return builder
35
+ }
36
+
37
+ /**
38
+ * Creates a new TransferBuilder instance.
39
+ * @param chainId - The blockchain network identifier
40
+ */
41
+ constructor(chainId: ChainId) {
42
+ super()
43
+ this.chainId = chainId
44
+ }
45
+
46
+ /**
47
+ * Adds a transfer to the intent.
48
+ * @param transfer - The transfer data configuration
49
+ * @returns This TransferBuilder instance for method chaining
50
+ */
51
+ addTransfer(transfer: TransferData): TransferBuilder {
52
+ this.transfers.push(transfer)
53
+ return this
54
+ }
55
+
56
+ /**
57
+ * Adds multiple transfers to the intent.
58
+ * @param transfers - Array of transfer data configurations
59
+ * @returns This TransferBuilder instance for method chaining
60
+ */
61
+ addTransfers(transfers: TransferData[]): TransferBuilder {
62
+ for (let i = 0; i < transfers.length; i++) this.transfers.push(transfers[i])
63
+ return this
64
+ }
65
+
66
+ /**
67
+ * Adds a transfer from a TokenAmount.
68
+ * @param tokenAmount - The token amount to transfer (must be on same chain)
69
+ * @param recipient - The address to receive the tokens
70
+ * @returns This TransferBuilder instance for method chaining
71
+ */
72
+ addTransferFromTokenAmount(tokenAmount: TokenAmount, recipient: Address): TransferBuilder {
73
+ if (tokenAmount.token.chainId !== this.chainId) throw new Error('Transfer tokens must be on the same chain')
74
+ return this.addTransfer(TransferData.fromTokenAmount(tokenAmount, recipient))
75
+ }
76
+
77
+ /**
78
+ * Adds a transfer from a 32-bit integer amount.
79
+ * @param token - The token to transfer (must be on same chain)
80
+ * @param amount - The amount as a whole number
81
+ * @param recipient - The address to receive the tokens
82
+ * @returns This TransferBuilder instance for method chaining
83
+ */
84
+ addTransferFromI32(token: Token, amount: i32, recipient: Address): TransferBuilder {
85
+ if (token.chainId !== this.chainId) throw new Error('Transfer tokens must be on the same chain')
86
+ return this.addTransfer(TransferData.fromI32(token, amount, recipient))
87
+ }
88
+
89
+ /**
90
+ * Adds a transfer from a BigInt amount.
91
+ * @param token - The token to transfer (must be on same chain)
92
+ * @param amount - The amount in the token's smallest unit
93
+ * @param recipient - The address to receive the tokens
94
+ * @returns This TransferBuilder instance for method chaining
95
+ */
96
+ addTransferFromBigInt(token: Token, amount: BigInt, recipient: Address): TransferBuilder {
97
+ if (token.chainId !== this.chainId) throw new Error('Transfer tokens must be on the same chain')
98
+ return this.addTransfer(TransferData.fromBigInt(token, amount, recipient))
99
+ }
100
+
101
+ /**
102
+ * Adds a transfer from a decimal string amount.
103
+ * @param token - The token to transfer (must be on same chain)
104
+ * @param amount - The amount as a decimal string
105
+ * @param recipient - The address to receive the tokens
106
+ * @returns This TransferBuilder instance for method chaining
107
+ */
108
+ addTransferFromStringDecimal(token: Token, amount: string, recipient: Address): TransferBuilder {
109
+ if (token.chainId !== this.chainId) throw new Error('Transfer tokens must be on the same chain')
110
+ return this.addTransfer(TransferData.fromStringDecimal(token, amount, recipient))
111
+ }
112
+
113
+ /**
114
+ * Adds multiple transfers from TokenAmounts to the same recipient.
115
+ * @param tokenAmounts - Array of token amounts to transfer (must be on same chain)
116
+ * @param recipient - The address to receive all the tokens
117
+ * @returns This TransferBuilder instance for method chaining
118
+ */
119
+ addTransfersFromTokenAmounts(tokenAmounts: TokenAmount[], recipient: Address): TransferBuilder {
120
+ for (let i = 0; i < tokenAmounts.length; i++) this.addTransferFromTokenAmount(tokenAmounts[i], recipient)
121
+ return this
122
+ }
123
+
124
+ /**
125
+ * Sets the fee for this transfer intent.
126
+ * @param fee - The fee token amount (must be on same chain)
127
+ * @returns This TransferBuilder instance for method chaining
128
+ */
129
+ addFee(fee: TokenAmount): TransferBuilder {
130
+ if (fee.token.chainId !== this.chainId) throw new Error('Fee token must be on the same chain')
131
+ this.fee = fee
132
+ return this
133
+ }
134
+
135
+ /**
136
+ * Sets the settler address for this intent.
137
+ * @param settler - The settler address as an Address instance
138
+ * @returns This TransferBuilder instance for method chaining
139
+ */
140
+ addSettler(settler: Address): TransferBuilder {
141
+ return changetype<TransferBuilder>(super.addSettler(settler))
142
+ }
143
+
144
+ /**
145
+ * Sets the settler address from a string.
146
+ * @param settler - The settler address as a hex string
147
+ * @returns This TransferBuilder instance for method chaining
148
+ */
149
+ addSettlerAsString(settler: string): TransferBuilder {
150
+ return changetype<TransferBuilder>(super.addSettlerAsString(settler))
151
+ }
152
+
153
+ /**
154
+ * Sets the deadline for this intent.
155
+ * @param deadline - The deadline as a timestamp
156
+ * @returns This TransferBuilder instance for method chaining
157
+ */
158
+ addDeadline(deadline: BigInt): TransferBuilder {
159
+ return changetype<TransferBuilder>(super.addDeadline(deadline))
160
+ }
161
+
162
+ /**
163
+ * Sets the user address for this intent.
164
+ * @param user - The user address
165
+ * @returns This TransferBuilder instance for method chaining
166
+ */
167
+ addUser(user: Address): TransferBuilder {
168
+ return changetype<TransferBuilder>(super.addUser(user))
169
+ }
170
+
171
+ /**
172
+ * Sets the user address from a string.
173
+ * @param user - The user address as a hex string
174
+ * @returns This TransferBuilder instance for method chaining
175
+ */
176
+ addUserAsString(user: string): TransferBuilder {
177
+ return changetype<TransferBuilder>(super.addUserAsString(user))
178
+ }
179
+
180
+ /**
181
+ * Sets the nonce for this intent.
182
+ * @param nonce - A unique identifier to prevent replay attacks
183
+ * @returns This TransferBuilder instance for method chaining
184
+ */
185
+ addNonce(nonce: string): TransferBuilder {
186
+ return changetype<TransferBuilder>(super.addNonce(nonce))
187
+ }
188
+
189
+ /**
190
+ * Builds and returns the final Transfer intent.
191
+ * @returns A new Transfer instance with all configured parameters
192
+ */
193
+ build(): Transfer {
194
+ if (!this.fee) throw new Error('Transfer fee must be specified')
195
+ return new Transfer(
196
+ this.chainId,
197
+ this.transfers,
198
+ this.fee as TokenAmount,
199
+ this.settler,
200
+ this.user,
201
+ this.deadline,
202
+ this.nonce
203
+ )
204
+ }
205
+ }
206
+
207
+ /**
208
+ * Represents transfer data for a single token transfer operation.
209
+ * Specifies the token, amount, and recipient for the transfer.
210
+ */
211
+ @json
212
+ export class TransferData {
213
+ public token: string
214
+ public amount: string
215
+ public recipient: string
216
+
217
+ /**
218
+ * Creates TransferData from a TokenAmount.
219
+ * @param tokenAmount - The token amount to transfer
220
+ * @param recipient - The address to receive the tokens
221
+ * @returns A new TransferData instance
222
+ */
223
+ static fromTokenAmount(tokenAmount: TokenAmount, recipient: Address): TransferData {
224
+ return new TransferData(tokenAmount.token.address, tokenAmount.amount, recipient)
225
+ }
226
+
227
+ /**
228
+ * Creates TransferData from a 32-bit integer amount.
229
+ * @param token - The token to transfer
230
+ * @param amount - The amount as a whole number
231
+ * @param recipient - The address to receive the tokens
232
+ * @returns A new TransferData instance
233
+ */
234
+ static fromI32(token: Token, amount: i32, recipient: Address): TransferData {
235
+ return this.fromTokenAmount(TokenAmount.fromI32(token, amount), recipient)
236
+ }
237
+
238
+ /**
239
+ * Creates TransferData from a BigInt amount.
240
+ * @param token - The token to transfer
241
+ * @param amount - The amount in the token's smallest unit
242
+ * @param recipient - The address to receive the tokens
243
+ * @returns A new TransferData instance
244
+ */
245
+ static fromBigInt(token: Token, amount: BigInt, recipient: Address): TransferData {
246
+ return this.fromTokenAmount(TokenAmount.fromBigInt(token, amount), recipient)
247
+ }
248
+
249
+ /**
250
+ * Creates TransferData from a decimal string amount.
251
+ * @param token - The token to transfer
252
+ * @param amount - The amount as a decimal string
253
+ * @param recipient - The address to receive the tokens
254
+ * @returns A new TransferData instance
255
+ */
256
+ static fromStringDecimal(token: Token, amount: string, recipient: Address): TransferData {
257
+ return this.fromTokenAmount(TokenAmount.fromStringDecimal(token, amount), recipient)
258
+ }
259
+
260
+ /**
261
+ * Creates a new TransferData instance.
262
+ * @param token - The token address
263
+ * @param amount - The amount in the token's smallest unit
264
+ * @param recipient - The address to receive the tokens
265
+ */
266
+ constructor(token: Address, amount: BigInt, recipient: Address) {
267
+ this.token = token.toString()
268
+ this.amount = amount.toString()
269
+ this.recipient = recipient.toString()
270
+ }
271
+ }
272
+
273
+ /**
274
+ * Represents a Transfer intent for sending tokens to recipients on a blockchain network.
275
+ */
276
+ @json
277
+ export class Transfer extends Intent {
278
+ public chainId: ChainId
279
+ public transfers: TransferData[]
280
+ public feeToken: string
281
+ public feeAmount: string
282
+
283
+ /**
284
+ * Creates a simple single-token transfer intent.
285
+ * @param chainId - The blockchain network identifier
286
+ * @param token - The token address to transfer
287
+ * @param amount - The amount to transfer
288
+ * @param recipient - The address to receive the tokens
289
+ * @param fee - The fee amount for the transfer
290
+ * @param settler - The settler address (optional)
291
+ * @param user - The user address (optional)
292
+ * @param deadline - The deadline timestamp (optional)
293
+ * @param nonce - The nonce for replay protection (optional)
294
+ * @returns A new Transfer instance
295
+ */
296
+ static create(
297
+ chainId: ChainId,
298
+ token: Address,
299
+ amount: BigInt,
300
+ recipient: Address,
301
+ fee: BigInt,
302
+ settler: Address | null = null,
303
+ user: Address | null = null,
304
+ deadline: BigInt | null = null,
305
+ nonce: string | null = null
306
+ ): Transfer {
307
+ const transferToken = Token.fromAddress(token, chainId)
308
+ const transferAmount = TokenAmount.fromBigInt(transferToken, amount)
309
+ const transferData = TransferData.fromTokenAmount(transferAmount, recipient)
310
+ const feeAmount = TokenAmount.fromBigInt(transferToken, fee)
311
+ return new Transfer(chainId, [transferData], feeAmount, settler, user, deadline, nonce)
312
+ }
313
+
314
+ /**
315
+ * Creates a new Transfer intent.
316
+ * @param chainId - The blockchain network identifier
317
+ * @param transfers - Array of transfer data configurations
318
+ * @param fee - The fee token amount for the transfers
319
+ * @param settler - The settler address (optional)
320
+ * @param user - The user address (optional)
321
+ * @param deadline - The deadline timestamp (optional)
322
+ * @param nonce - The nonce for replay protection (optional)
323
+ */
324
+ constructor(
325
+ chainId: ChainId,
326
+ transfers: TransferData[],
327
+ fee: TokenAmount,
328
+ settler: Address | null = null,
329
+ user: Address | null = null,
330
+ deadline: BigInt | null = null,
331
+ nonce: string | null = null
332
+ ) {
333
+ super(OperationType.Transfer, settler, user, deadline, nonce)
334
+
335
+ if (transfers.length === 0) throw new Error('Transfer list cannot be empty')
336
+
337
+ this.transfers = transfers
338
+ this.feeToken = fee.token.address.toString()
339
+ this.feeAmount = fee.amount.toString()
340
+ this.chainId = chainId
341
+ }
342
+
343
+ /**
344
+ * Sends this Transfer intent to the execution environment.
345
+ */
346
+ send(): void {
347
+ environment.transfer(this)
348
+ }
349
+ }
@@ -0,0 +1,4 @@
1
+ export * from './Call'
2
+ export * from './Intent'
3
+ export * from './Swap'
4
+ export * from './Transfer'
@@ -0,0 +1,16 @@
1
+ import { Address, ChainId } from '../types'
2
+
3
+ @json
4
+ export class Call {
5
+ public to: string
6
+ public chainId: ChainId
7
+ public timestamp: Date | null
8
+ public data: string
9
+
10
+ constructor(to: Address, chainId: ChainId, timestamp: Date | null, data: string) {
11
+ this.to = to.toString()
12
+ this.chainId = chainId
13
+ this.data = data
14
+ this.timestamp = timestamp
15
+ }
16
+ }
@@ -0,0 +1 @@
1
+ export * from './Call'
@@ -0,0 +1,191 @@
1
+ import { environment } from '../environment'
2
+ import { evm } from '../evm'
3
+ import { join, NATIVE_ADDRESS, parseCSV, Serializable, serialize } from '../helpers'
4
+ import { Address, ChainId, EvmDecodeParam } from '../types'
5
+
6
+ /**
7
+ * Represents a token on a blockchain network including data like symbol, decimals, and address.
8
+ * Supports both ERC-20 and native tokens.
9
+ */
10
+ export class Token implements Serializable {
11
+ public static readonly EMPTY_DECIMALS: u8 = u8.MAX_VALUE
12
+ public static readonly EMPTY_SYMBOL: string = ''
13
+ private static readonly SERIALIZED_PREFIX: string = 'Token'
14
+
15
+ private _symbol: string
16
+ private _address: Address
17
+ private _chainId: ChainId
18
+ private _decimals: u8
19
+ private _timestamp: Date | null = null
20
+
21
+ /**
22
+ * Creates a Token instance from an Address object.
23
+ * @param address - The contract address of the token
24
+ * @param chainId - The blockchain network identifier
25
+ * @param decimals - Number of decimal places (optional, will be queried if not provided)
26
+ * @param symbol - Token symbol (optional, will be queried if not provided)
27
+ * @param timestamp - Timestamp for historical queries (optional)
28
+ * @returns A new Token instance
29
+ */
30
+ static fromAddress(
31
+ address: Address,
32
+ chainId: ChainId,
33
+ decimals: u8 = Token.EMPTY_DECIMALS,
34
+ symbol: string = Token.EMPTY_SYMBOL,
35
+ timestamp: Date | null = null
36
+ ): Token {
37
+ return new Token(address, chainId, decimals, symbol, timestamp)
38
+ }
39
+
40
+ /**
41
+ * Creates a Token instance from a string address.
42
+ * @param address - The contract address as a hex string
43
+ * @param chainId - The blockchain network identifier
44
+ * @param decimals - Number of decimal places (optional, will be queried if not provided)
45
+ * @param symbol - Token symbol (optional, will be queried if not provided)
46
+ * @param timestamp - Timestamp for historical queries (optional)
47
+ * @returns A new Token instance
48
+ */
49
+ static fromString(
50
+ address: string,
51
+ chainId: ChainId,
52
+ decimals: u8 = Token.EMPTY_DECIMALS,
53
+ symbol: string = Token.EMPTY_SYMBOL,
54
+ timestamp: Date | null = null
55
+ ): Token {
56
+ return Token.fromAddress(Address.fromString(address), chainId, decimals, symbol, timestamp)
57
+ }
58
+
59
+ /**
60
+ * Creates a Token instance representing the native token of the specified chain.
61
+ * @param chainId - The blockchain network identifier
62
+ * @returns A new Token instance for the native token
63
+ */
64
+ static native(chainId: ChainId): Token {
65
+ switch (chainId) {
66
+ case ChainId.ETHEREUM:
67
+ case ChainId.OPTIMISM:
68
+ return Token.fromString(NATIVE_ADDRESS, chainId, 18, 'ETH')
69
+ case ChainId.POLYGON:
70
+ return Token.fromString(NATIVE_ADDRESS, chainId, 18, 'POL')
71
+ default:
72
+ throw new Error(`Unsupported chainId: ${chainId}`)
73
+ }
74
+ }
75
+
76
+ /**
77
+ * Parses a serialized Token string and creates a Token instance.
78
+ * @param serialized - The serialized token string in format: Token(address,chainId)
79
+ * @returns A new Token instance parsed from the serialized data
80
+ */
81
+ static parse(serialized: string): Token {
82
+ const isToken = serialized.startsWith(`${Token.SERIALIZED_PREFIX}(`) && serialized.endsWith(')')
83
+ if (!isToken) throw new Error('Invalid serialized token')
84
+
85
+ const elements = parseCSV(serialized.slice(Token.SERIALIZED_PREFIX.length + 1, -1))
86
+ const areNull = elements.some((element) => element === null)
87
+ if (areNull) throw new Error('Invalid serialized token')
88
+
89
+ const address = elements[0]!
90
+ const chainId = i32.parse(elements[1]!)
91
+
92
+ return Token.fromString(address, chainId)
93
+ }
94
+
95
+ /**
96
+ * Creates a new Token instance.
97
+ * @param address - The contract address of the token
98
+ * @param chainId - The blockchain network identifier
99
+ * @param decimals - Number of decimal places (optional, will be queried if not provided)
100
+ * @param symbol - Token symbol (optional, will be queried if not provided)
101
+ * @param timestamp - Timestamp for historical queries (optional)
102
+ */
103
+ constructor(
104
+ address: Address,
105
+ chainId: ChainId,
106
+ decimals: u8 = Token.EMPTY_DECIMALS,
107
+ symbol: string = Token.EMPTY_SYMBOL,
108
+ timestamp: Date | null = null
109
+ ) {
110
+ this._address = address
111
+ this._chainId = chainId
112
+ this._timestamp = timestamp
113
+ this._symbol = symbol
114
+ this._decimals = decimals
115
+ // Ensure symbol and decimals are set for native tokens.
116
+ // Since queries return only the address and chainId, missing metadata must be filled
117
+ // to prevent the symbol and decimals getters from failing for native tokens.
118
+ if (
119
+ this._address.equals(Address.fromString(NATIVE_ADDRESS)) &&
120
+ (this._symbol === Token.EMPTY_SYMBOL || this._decimals === Token.EMPTY_DECIMALS)
121
+ ) {
122
+ const nativeToken = Token.native(this._chainId)
123
+ if (this._symbol === Token.EMPTY_SYMBOL) {
124
+ this._symbol = nativeToken.symbol
125
+ }
126
+ if (this._decimals === Token.EMPTY_DECIMALS) {
127
+ this._decimals = nativeToken.decimals
128
+ }
129
+ }
130
+ }
131
+
132
+ get address(): Address {
133
+ return this._address.clone()
134
+ }
135
+
136
+ get chainId(): ChainId {
137
+ return this._chainId
138
+ }
139
+
140
+ get symbol(): string {
141
+ if (this._symbol === Token.EMPTY_SYMBOL) {
142
+ const response = environment.contractCall(this.address, this.chainId, this._timestamp, '0x95d89b41')
143
+ this._symbol = evm.decode(new EvmDecodeParam('string', response))
144
+ }
145
+ return this._symbol
146
+ }
147
+
148
+ get decimals(): u8 {
149
+ if (this._decimals == Token.EMPTY_DECIMALS) {
150
+ const result = environment.contractCall(this.address, this.chainId, this._timestamp, '0x313ce567')
151
+ this._decimals = u8.parse(evm.decode(new EvmDecodeParam('uint8', result)))
152
+ }
153
+ return this._decimals
154
+ }
155
+
156
+ set decimals(value: u8) {
157
+ this._decimals = value
158
+ }
159
+
160
+ set symbol(value: string) {
161
+ this._symbol = value
162
+ }
163
+
164
+ set timestamp(value: Date) {
165
+ this._timestamp = value
166
+ }
167
+
168
+ /**
169
+ * Checks if this token is equal to another token.
170
+ * Tokens are considered equal if they have the same address on the same chain.
171
+ * @param other - The token to compare with
172
+ * @returns True if both tokens represent the same asset
173
+ */
174
+ equals(other: Token): boolean {
175
+ const isSameChain = this.chainId === other.chainId
176
+ const isSameAddress = this.address.equals(other.address)
177
+ return isSameChain && isSameAddress
178
+ }
179
+
180
+ /**
181
+ * Tells the string representation of this token.
182
+ * @returns The token symbol
183
+ */
184
+ toString(): string {
185
+ return this.symbol
186
+ }
187
+
188
+ serialize(): string {
189
+ return `${Token.SERIALIZED_PREFIX}(${join([serialize(this.address), serialize(this.chainId)])})`
190
+ }
191
+ }