@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 +2 -2
- package/package.json +3 -2
- package/src/chains/Arbitrum.ts +7 -7
- package/src/chains/Base.ts +7 -7
- package/src/chains/Ethereum.ts +7 -7
- package/src/chains/Gnosis.ts +7 -7
- package/src/chains/Optimism.ts +7 -7
- package/src/context/Context.ts +3 -1
- package/src/environment.ts +11 -7
- package/src/helpers/constants.ts +1 -0
- package/src/helpers/strings.ts +31 -0
- package/src/intents/Call.ts +19 -45
- package/src/intents/Intent.ts +64 -0
- package/src/intents/Swap.ts +25 -10
- package/src/intents/Transfer.ts +26 -52
- package/src/queries/GetPrice.ts +2 -2
- package/src/queries/GetRelevantTokens.ts +4 -4
- package/src/tokens/DenominationToken.ts +62 -0
- package/src/tokens/ERC20Token.ts +181 -0
- package/src/tokens/Token.ts +46 -130
- package/src/tokens/TokenAmount.ts +8 -0
- package/src/tokens/index.ts +2 -0
- package/src/types/Address.ts +68 -12
- package/src/types/ByteArray.ts +17 -2
- package/src/types/Bytes.ts +7 -0
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,
|
|
56
|
+
import { environment, ERC20Token } from '@mimicprotocol/lib-ts'
|
|
57
57
|
|
|
58
|
-
const USDC =
|
|
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.
|
|
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
|
+
}
|
package/src/chains/Arbitrum.ts
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import {
|
|
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 =
|
|
9
|
-
export const USDC =
|
|
10
|
-
export const USDT =
|
|
11
|
-
export const DAI =
|
|
12
|
-
export const WBTC =
|
|
13
|
-
export const 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
|
}
|
package/src/chains/Base.ts
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import {
|
|
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 =
|
|
9
|
-
export const USDC =
|
|
10
|
-
export const USDT =
|
|
11
|
-
export const DAI =
|
|
12
|
-
export const WBTC =
|
|
13
|
-
export const 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
|
}
|
package/src/chains/Ethereum.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import {
|
|
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 =
|
|
7
|
-
export const USDC =
|
|
8
|
-
export const USDT =
|
|
9
|
-
export const DAI =
|
|
10
|
-
export const WBTC =
|
|
11
|
-
export const 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
|
}
|
package/src/chains/Gnosis.ts
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import {
|
|
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 =
|
|
9
|
-
export const USDC =
|
|
10
|
-
export const USDT =
|
|
11
|
-
export const WXDAI =
|
|
12
|
-
export const WBTC =
|
|
13
|
-
export const 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
|
}
|
package/src/chains/Optimism.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import {
|
|
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 =
|
|
7
|
-
export const USDC =
|
|
8
|
-
export const USDT =
|
|
9
|
-
export const DAI =
|
|
10
|
-
export const WBTC =
|
|
11
|
-
export const 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
|
}
|
package/src/context/Context.ts
CHANGED
|
@@ -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)
|
|
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
|
}
|
package/src/environment.ts
CHANGED
|
@@ -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 {
|
|
7
|
-
import {
|
|
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
|
-
|
|
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:
|
|
83
|
+
tokensList: ERC20Token[] = [],
|
|
80
84
|
listType: ListType = ListType.DenyList,
|
|
81
85
|
timestamp: Date | null = null
|
|
82
86
|
): TokenAmount[] {
|
package/src/helpers/constants.ts
CHANGED
package/src/helpers/strings.ts
CHANGED
|
@@ -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
|
+
}
|
package/src/intents/Call.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
|
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
|
-
|
|
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],
|
|
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
|
|
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
|
-
|
|
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
|
|
package/src/intents/Intent.ts
CHANGED
|
@@ -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
|
}
|
package/src/intents/Swap.ts
CHANGED
|
@@ -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.
|
|
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.
|
|
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.
|
|
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.
|
|
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(
|
|
383
|
-
const swapOut = TokenOut.fromBigInt(
|
|
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
|
}
|