@defisaver/sdk 1.3.36 → 1.3.37

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 (169) hide show
  1. package/README.md +55 -9
  2. package/docs/DEV.md +63 -0
  3. package/esm/src/Action.js +21 -13
  4. package/esm/src/ActionWithL2.js +4 -0
  5. package/esm/src/Recipe.js +5 -1
  6. package/esm/src/addresses.js +12 -12
  7. package/package.json +2 -6
  8. package/src/Action.ts +18 -9
  9. package/src/ActionWithL2.ts +2 -0
  10. package/src/Recipe.ts +4 -1
  11. package/src/addresses.ts +12 -12
  12. package/test/Action.js +83 -3
  13. package/test/ActionWithL2.js +65 -19
  14. package/test/Recipe.js +97 -0
  15. package/tsconfig.json +1 -5
  16. package/umd/index.js +36 -25
  17. package/ACTIONS.md +0 -1765
  18. package/DEV.md +0 -43
  19. package/docs/md/.nojekyll +0 -1
  20. package/docs/md/README.md +0 -12
  21. package/docs/md/classes/AaveBorrowAction.md +0 -458
  22. package/docs/md/classes/AaveClaimStkAaveAction.md +0 -455
  23. package/docs/md/classes/AaveCollateralSwitchAction.md +0 -455
  24. package/docs/md/classes/AavePaybackAction.md +0 -458
  25. package/docs/md/classes/AaveSupplyAction.md +0 -458
  26. package/docs/md/classes/AaveV2FlashLoanAction.md +0 -458
  27. package/docs/md/classes/AaveV2FlashLoanPaybackAction.md +0 -454
  28. package/docs/md/classes/AaveV3ATokenPaybackAction.md +0 -644
  29. package/docs/md/classes/AaveV3BorrowAction.md +0 -606
  30. package/docs/md/classes/AaveV3ClaimRewardsAction.md +0 -603
  31. package/docs/md/classes/AaveV3CollateralSwitchAction.md +0 -603
  32. package/docs/md/classes/AaveV3FlashLoanAction.md +0 -604
  33. package/docs/md/classes/AaveV3FlashLoanPaybackAction.md +0 -454
  34. package/docs/md/classes/AaveV3PaybackAction.md +0 -618
  35. package/docs/md/classes/AaveV3RatioCheckAction.md +0 -454
  36. package/docs/md/classes/AaveV3RatioTrigger.md +0 -454
  37. package/docs/md/classes/AaveV3SetEModeAction.md +0 -601
  38. package/docs/md/classes/AaveV3SupplyAction.md +0 -618
  39. package/docs/md/classes/AaveV3SwapBorrowRateModeAction.md +0 -602
  40. package/docs/md/classes/AaveV3WithdrawAction.md +0 -603
  41. package/docs/md/classes/AaveWithdrawAction.md +0 -456
  42. package/docs/md/classes/Action.md +0 -604
  43. package/docs/md/classes/ActionWithL2.md +0 -612
  44. package/docs/md/classes/AutomationV2Unsub.md +0 -452
  45. package/docs/md/classes/BalancerFlashLoanAction.md +0 -456
  46. package/docs/md/classes/BalancerFlashLoanPaybackAction.md +0 -454
  47. package/docs/md/classes/BalancerV2ClaimAction.md +0 -457
  48. package/docs/md/classes/BalancerV2SupplyAction.md +0 -458
  49. package/docs/md/classes/BalancerV2WithdrawAction.md +0 -470
  50. package/docs/md/classes/CBChickenInAction.md +0 -454
  51. package/docs/md/classes/CBChickenOutAction.md +0 -455
  52. package/docs/md/classes/CBCreateAction.md +0 -454
  53. package/docs/md/classes/CBRedeemAction.md +0 -456
  54. package/docs/md/classes/ChainLinkPriceTrigger.md +0 -453
  55. package/docs/md/classes/ChangeProxyOwnerAction.md +0 -453
  56. package/docs/md/classes/CompV3RatioTrigger.md +0 -454
  57. package/docs/md/classes/CompoundBorrowAction.md +0 -455
  58. package/docs/md/classes/CompoundClaimAction.md +0 -456
  59. package/docs/md/classes/CompoundCollateralSwitchAction.md +0 -454
  60. package/docs/md/classes/CompoundGetDebtAction.md +0 -452
  61. package/docs/md/classes/CompoundPaybackAction.md +0 -456
  62. package/docs/md/classes/CompoundRatioTrigger.md +0 -453
  63. package/docs/md/classes/CompoundSupplyAction.md +0 -456
  64. package/docs/md/classes/CompoundV3AllowAction.md +0 -455
  65. package/docs/md/classes/CompoundV3BorrowAction.md +0 -456
  66. package/docs/md/classes/CompoundV3ClaimAction.md +0 -456
  67. package/docs/md/classes/CompoundV3PaybackAction.md +0 -468
  68. package/docs/md/classes/CompoundV3RatioCheckAction.md +0 -456
  69. package/docs/md/classes/CompoundV3SupplyAction.md +0 -468
  70. package/docs/md/classes/CompoundV3TransferAction.md +0 -457
  71. package/docs/md/classes/CompoundV3WithdrawAction.md +0 -457
  72. package/docs/md/classes/CompoundWithdrawAction.md +0 -455
  73. package/docs/md/classes/ConvexClaimAction.md +0 -466
  74. package/docs/md/classes/ConvexDepositAction.md +0 -468
  75. package/docs/md/classes/ConvexWithdrawAction.md +0 -468
  76. package/docs/md/classes/CurveClaimFeesAction.md +0 -452
  77. package/docs/md/classes/CurveDepositAction.md +0 -467
  78. package/docs/md/classes/CurveGaugeDepositAction.md +0 -455
  79. package/docs/md/classes/CurveGaugeWithdrawAction.md +0 -454
  80. package/docs/md/classes/CurveMintCrvAction.md +0 -452
  81. package/docs/md/classes/CurveStethPoolDepositAction.md +0 -456
  82. package/docs/md/classes/CurveStethPoolWithdrawAction.md +0 -457
  83. package/docs/md/classes/CurveSwapAction.md +0 -457
  84. package/docs/md/classes/CurveWithdrawAction.md +0 -468
  85. package/docs/md/classes/DfsWeb3.md +0 -212
  86. package/docs/md/classes/DyDxFlashLoanAction.md +0 -456
  87. package/docs/md/classes/DyDxFlashLoanPaybackAction.md +0 -454
  88. package/docs/md/classes/DyDxWithdrawAction.md +0 -455
  89. package/docs/md/classes/EulerFlashLoanAction.md +0 -456
  90. package/docs/md/classes/EulerFlashLoanPaybackAction.md +0 -454
  91. package/docs/md/classes/GUniDeposit.md +0 -461
  92. package/docs/md/classes/GUniWithdraw.md +0 -458
  93. package/docs/md/classes/GasFeeAction.md +0 -454
  94. package/docs/md/classes/GasFeeActionL2.md +0 -455
  95. package/docs/md/classes/GasPriceTrigger.md +0 -35
  96. package/docs/md/classes/InstPullTokensAction.md +0 -454
  97. package/docs/md/classes/LidoStakeAction.md +0 -455
  98. package/docs/md/classes/LidoUnwrapAction.md +0 -455
  99. package/docs/md/classes/LidoWrapAction.md +0 -456
  100. package/docs/md/classes/LiquityBorrowAction.md +0 -457
  101. package/docs/md/classes/LiquityClaimAction.md +0 -453
  102. package/docs/md/classes/LiquityClaimSPRewardsAction.md +0 -454
  103. package/docs/md/classes/LiquityClaimStakingRewardsAction.md +0 -454
  104. package/docs/md/classes/LiquityCloseAction.md +0 -454
  105. package/docs/md/classes/LiquityEthGainToTroveAction.md +0 -455
  106. package/docs/md/classes/LiquityOpenAction.md +0 -459
  107. package/docs/md/classes/LiquityPaybackAction.md +0 -456
  108. package/docs/md/classes/LiquityRatioTrigger.md +0 -453
  109. package/docs/md/classes/LiquityRedeemAction.md +0 -461
  110. package/docs/md/classes/LiquitySPDepositAction.md +0 -456
  111. package/docs/md/classes/LiquitySPWithdrawAction.md +0 -458
  112. package/docs/md/classes/LiquityStakeAction.md +0 -456
  113. package/docs/md/classes/LiquitySupplyAction.md +0 -456
  114. package/docs/md/classes/LiquityUnstakeAction.md +0 -458
  115. package/docs/md/classes/LiquityWithdrawAction.md +0 -456
  116. package/docs/md/classes/MStableClaimAction.md +0 -456
  117. package/docs/md/classes/MStableDepositAction.md +0 -461
  118. package/docs/md/classes/MStableWithdrawAction.md +0 -461
  119. package/docs/md/classes/MakerClaimAction.md +0 -455
  120. package/docs/md/classes/MakerFlashLoanAction.md +0 -455
  121. package/docs/md/classes/MakerFlashLoanPaybackAction.md +0 -454
  122. package/docs/md/classes/MakerGenerateAction.md +0 -456
  123. package/docs/md/classes/MakerGiveAction.md +0 -456
  124. package/docs/md/classes/MakerMergeAction.md +0 -455
  125. package/docs/md/classes/MakerOpenVaultAction.md +0 -454
  126. package/docs/md/classes/MakerPaybackAction.md +0 -456
  127. package/docs/md/classes/MakerRatioAction.md +0 -453
  128. package/docs/md/classes/MakerRatioCheckAction.md +0 -457
  129. package/docs/md/classes/MakerRatioTrigger.md +0 -453
  130. package/docs/md/classes/MakerSupplyAction.md +0 -457
  131. package/docs/md/classes/MakerWithdrawAction.md +0 -457
  132. package/docs/md/classes/PullTokenAction.md +0 -455
  133. package/docs/md/classes/RariDepositAction.md +0 -458
  134. package/docs/md/classes/RariWithdrawAction.md +0 -459
  135. package/docs/md/classes/Recipe.md +0 -195
  136. package/docs/md/classes/ReflexerGenerateAction.md +0 -455
  137. package/docs/md/classes/ReflexerNativeUniV2SaviourDepositAction.md +0 -455
  138. package/docs/md/classes/ReflexerNativeUniV2SaviourGetReservesAction.md +0 -454
  139. package/docs/md/classes/ReflexerNativeUniV2SaviourWithdrawAction.md +0 -455
  140. package/docs/md/classes/ReflexerOpenSafeAction.md +0 -453
  141. package/docs/md/classes/ReflexerPaybackAction.md +0 -455
  142. package/docs/md/classes/ReflexerRatioTrigger.md +0 -453
  143. package/docs/md/classes/ReflexerSupplyAction.md +0 -456
  144. package/docs/md/classes/ReflexerWithdrawAction.md +0 -456
  145. package/docs/md/classes/SellAction.md +0 -613
  146. package/docs/md/classes/SendTokenAction.md +0 -467
  147. package/docs/md/classes/SendTokenAndUnwrapAction.md +0 -455
  148. package/docs/md/classes/Strategy.md +0 -195
  149. package/docs/md/classes/SubInputsAction.md +0 -454
  150. package/docs/md/classes/SumInputsAction.md +0 -454
  151. package/docs/md/classes/TimestampTrigger.md +0 -451
  152. package/docs/md/classes/ToggleSubAction.md +0 -454
  153. package/docs/md/classes/TokenBalanceAction.md +0 -452
  154. package/docs/md/classes/TrailingStopTrigger.md +0 -453
  155. package/docs/md/classes/TransferNFTAction.md +0 -454
  156. package/docs/md/classes/UniV3CurrentTickTrigger.md +0 -452
  157. package/docs/md/classes/UniswapSupplyAction.md +0 -461
  158. package/docs/md/classes/UniswapV3CollectAction.md +0 -614
  159. package/docs/md/classes/UniswapV3CreatePoolAction.md +0 -611
  160. package/docs/md/classes/UniswapV3MintAction.md +0 -610
  161. package/docs/md/classes/UniswapV3SupplyAction.md +0 -63
  162. package/docs/md/classes/UniswapV3WithdrawAction.md +0 -618
  163. package/docs/md/classes/UniswapWithdrawAction.md +0 -460
  164. package/docs/md/classes/UnwrapEthAction.md +0 -600
  165. package/docs/md/classes/UpdateSubAction.md +0 -454
  166. package/docs/md/classes/WrapEthAction.md +0 -599
  167. package/docs/md/classes/YearnSupplyAction.md +0 -456
  168. package/docs/md/classes/YearnWithdrawAction.md +0 -456
  169. package/docs/md/modules.md +0 -814
package/README.md CHANGED
@@ -1,16 +1,62 @@
1
- # DeFi Saver Recipe SDK
1
+ # DeFi Saver SDK
2
2
 
3
- Create complex transactions that interact with multiple DeFi protocols.
3
+ Build actions and combine them into recipes for DeFi Saver smart wallets.
4
4
 
5
- ---
5
+ ## Installation
6
6
 
7
- - [General docs](https://docs.defisaver.com/)
8
- - [SDK docs](https://github.com/DecenterApps/defisaver-sdk/blob/master/DEV.md)
9
- - [Available Actions](https://github.com/DecenterApps/defisaver-sdk/blob/master/ACTIONS.md)
10
- - [Pre-made recipes - TODO](https://github.com/DecenterApps/defisaver-sdk/blob/master/RECIPES.md)
7
+ ```sh
8
+ npm install @defisaver/sdk
9
+ ```
10
+
11
+ ## Example
12
+
13
+ Encode a recipe that wraps 0.1 ETH into WETH on Ethereum:
14
+
15
+ ```js
16
+ const { configure, Recipe, actions } = require('@defisaver/sdk');
17
+
18
+ async function main() {
19
+ configure({ chainId: 1, testingMode: false });
20
+
21
+ const recipe = new Recipe('Wrap ETH', [
22
+ new actions.basic.WrapEthAction('100000000000000000'),
23
+ ]);
24
+
25
+ const [target, data] = recipe.encodeForDsProxyCall();
26
+ const value = await recipe.getEthValue();
27
+ console.log({ target, data, value });
28
+ }
29
+
30
+ main().catch(console.error);
31
+ ```
32
+
33
+ Amounts use token base units as decimal strings. Here, `value` is the ETH amount in wei. `target` and `data` are arguments for the smart wallet's `execute(address,bytes)` call; encoding does not submit a transaction.
34
+
35
+ - [Developer guide](docs/DEV.md): recipes, approvals, configuration, and contributing.
36
+ - [Action implementations](src/actions): current constructors and parameter documentation, grouped by protocol.
37
+ - [DeFi Saver documentation](https://docs.defisaver.com/): product and protocol guides.
11
38
 
12
39
  ## Testing
13
40
 
14
- `npm run test` - Run all tests
41
+ From a local checkout, install dependencies with `npm ci`, then run:
42
+
43
+ ```sh
44
+ # Run offline tests (no RPC required)
45
+ npm run test:unit
46
+
47
+ # Run all tests, including RPC-dependent wallet tests
48
+ npm test
49
+
50
+ # Run a single test file (test/Recipe.js)
51
+ npm run test-single --name=Recipe
52
+ ```
53
+
54
+ The full suite requires `RPC` in your environment or a local `.env` file for the wallet tests. Point it at a local or forked test chain.
55
+
56
+ ## Building
57
+
58
+ ```sh
59
+ npm run build
60
+ ```
15
61
 
16
- `npm run test-single --name=your_test_name` - Run single test for specified name e.g. for MyTest.js test name is MyTest
62
+ Builds the package and runs lint with automatic fixes. To check lint without changing files, use `npm run lint-check`.
package/docs/DEV.md ADDED
@@ -0,0 +1,63 @@
1
+ # Developer guide
2
+
3
+ The SDK models a transaction as an `Action`, or a sequence of actions as a `Recipe`. Action constructors describe contract inputs; the shared classes handle encoding and composition. See the [README](../README.md) for installation and a minimal example.
4
+
5
+ ## Configuration and amounts
6
+
7
+ Call `configure({ chainId: 1, testingMode: false })` before constructing actions or recipes. Addresses are resolved during construction. Configuration is global, including the token library's network: avoid switching chains while working with existing objects or concurrent operations.
8
+
9
+ Use decimal strings in token base units: `'1000000'` represents 1 token with 6 decimals, and `'1000000000000000000'` represents 1 token with 18 decimals. Avoid JavaScript numbers for large amounts. Special values such as a maximum amount are action-specific; check the constructor documentation.
10
+
11
+ Supported networks are exported as `networks`. Use `getAddr(name, chainId)` to look up a deployment; it throws when the chain or named deployment is unavailable. Network support does not imply every action is deployed there.
12
+
13
+ ## Composing a recipe
14
+
15
+ This example encodes pulling 1 WETH from an owner into the wallet and returning the amount received to that owner. Replace the example owner with your address when integrating it.
16
+
17
+ ```js
18
+ const { configure, Recipe, actions } = require('@defisaver/sdk');
19
+
20
+ async function main() {
21
+ configure({ chainId: 1, testingMode: false });
22
+
23
+ const owner = '0x0000000000000000000000000000000000000001';
24
+ const weth = '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2';
25
+ const recipe = new Recipe('Pull and return WETH', [
26
+ new actions.basic.PullTokenAction(weth, owner, '1000000000000000000'),
27
+ new actions.basic.SendTokenAction(weth, owner, '$1'),
28
+ ]);
29
+
30
+ const approvals = await recipe.getAssetsToApprove();
31
+ const value = await recipe.getEthValue();
32
+ const [target, data] = recipe.encodeForDsProxyCall();
33
+ console.log({ approvals, target, data, value });
34
+ }
35
+
36
+ main().catch(console.error);
37
+ ```
38
+
39
+ `'$1'` refers to the first action's return value, with numbering starting at **1**. References must point to an earlier action and be used in an input that supports mapping. A return value's meaning depends on the action; it is not necessarily a token amount. Nested inputs can have a custom mapping order, defined by the action's `mappableArgs`.
40
+
41
+ `getAssetsToApprove()` describes assets and owners requiring approval. For the ERC-20 example above, the owner approves the executing smart wallet to spend WETH before execution. The returned metadata does not perform approvals or check existing allowances. NFT approvals use different fields; inspect the returned type when supporting them.
42
+
43
+ For a DSProxy, pass `target` and `data` to its `execute(address,bytes)` method and attach `value` to that transaction. Other wallet integrations must use their appropriate execution path. Do not treat the returned target as a standalone user transaction destination.
44
+
45
+ A single action also exposes `encodeForDsProxyCall()`, `getAssetsToApprove()`, and `getEthValue()`. `ActionWithL2` supports specialized packed calldata; callers should normally use the public encoding methods rather than pack bytes themselves.
46
+
47
+ ## Strategies
48
+
49
+ `Strategy` describes reusable automation: add subscription slots with `addSubSlot`, triggers with `addTrigger`, and actions with `addAction`. Strategy inputs can refer to named subscription slots such as `'&vaultId'`, or the special addresses `'&proxy'` and `'&eoa'`. Action return references still use `'$1'`, `'$2'`, and so on.
50
+
51
+ Strategy encoding is separate from recipe execution. See the [strategy example](../test/Strategy.js) and [implementation](../src/Strategy.ts) for the current API.
52
+
53
+ ## Adding an action
54
+
55
+ 1. Add a class under the relevant [protocol directory](../src/actions), extending `Action` or `ActionWithL2`.
56
+ 2. Match the contract's action name, ABI types, and argument order in the `super` call. Define `mappableArgs` in the order expected by the contract.
57
+ 3. Implement approval metadata and ETH value when needed. Implement packed L2 encoding only when the contract supports it.
58
+ 4. Export the class through the protocol index and add its deployments to [addresses.ts](../src/addresses.ts).
59
+ 5. Add tests for encoded values, mappings, approvals, and ETH value, including relevant boundary cases. Keep constructor parameter documentation beside the implementation.
60
+
61
+ [PullTokenAction](../src/actions/basic/PullTokenAction.ts) is a small example. Use existing actions for structure, and the corresponding contract as the source of truth for input semantics.
62
+
63
+ See the [README](../README.md#testing) for testing commands.
package/esm/src/Action.js CHANGED
@@ -12,7 +12,7 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
12
12
  if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
13
13
  return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
14
14
  };
15
- var _Action_instances, _Action__getArgumentMappingWithSlots, _Action__getPlaceholderForType;
15
+ var _Action_instances, _Action__getReturnValueReference, _Action__getArgumentMappingWithSlots, _Action__getPlaceholderForType;
16
16
  import AbiCoder from 'web3-eth-abi';
17
17
  import { keccak256, padLeft, toHex } from 'web3-utils';
18
18
  import { CONFIG } from './config';
@@ -52,12 +52,12 @@ export class Action {
52
52
  */
53
53
  _getArgumentMapping() {
54
54
  return this.mappableArgs.map(arg => {
55
- if (new RegExp(/\$\d+/).test(arg)) {
56
- if (Array.isArray(arg))
57
- throw TypeError('Input can\'t be mapped to arrays (tuples/structs). Specify `mappableArgs` array in constructor.');
58
- // eslint-disable-next-line
59
- return parseInt(arg.substr(1));
55
+ if (Array.isArray(arg) && /\$\d+/.test(arg.toString())) {
56
+ throw TypeError('Input can\'t be mapped to arrays (tuples/structs). Specify `mappableArgs` array in constructor.');
60
57
  }
58
+ const reference = __classPrivateFieldGet(this, _Action_instances, "m", _Action__getReturnValueReference).call(this, arg);
59
+ if (reference !== undefined)
60
+ return reference;
61
61
  return 0;
62
62
  });
63
63
  }
@@ -100,7 +100,7 @@ export class Action {
100
100
  if (Array.isArray(arg))
101
101
  return arg.map((_arg, i) => this._replaceWithPlaceholders(_arg, paramTypeParsed[i]));
102
102
  if (typeof (paramType) === 'string') {
103
- if (new RegExp(/\$\d+/).test(arg))
103
+ if (__classPrivateFieldGet(this, _Action_instances, "m", _Action__getReturnValueReference).call(this, arg) !== undefined)
104
104
  return __classPrivateFieldGet(this, _Action_instances, "m", _Action__getPlaceholderForType).call(this, paramType);
105
105
  if (new RegExp(/&\w+/).test(arg))
106
106
  return __classPrivateFieldGet(this, _Action_instances, "m", _Action__getPlaceholderForType).call(this, paramType);
@@ -196,14 +196,22 @@ export class Action {
196
196
  }));
197
197
  }
198
198
  }
199
- _Action_instances = new WeakSet(), _Action__getArgumentMappingWithSlots = function _Action__getArgumentMappingWithSlots(subSlots) {
199
+ _Action_instances = new WeakSet(), _Action__getReturnValueReference = function _Action__getReturnValueReference(arg) {
200
+ if (typeof arg !== 'string' || !/\$\d+/.test(arg))
201
+ return undefined;
202
+ const index = Number(arg.slice(1));
203
+ if (!/^\$\d+$/.test(arg) || !Number.isSafeInteger(index) || index < 1) {
204
+ throw new Error(`Invalid action return reference: ${arg}`);
205
+ }
206
+ return index;
207
+ }, _Action__getArgumentMappingWithSlots = function _Action__getArgumentMappingWithSlots(subSlots) {
200
208
  return this.mappableArgs.map(arg => {
201
- if (new RegExp(/\$\d+/).test(arg)) {
202
- if (Array.isArray(arg))
203
- throw TypeError('Input can\'t be mapped to arrays (tuples/structs). Specify `mappableArgs` array in constructor.');
204
- // eslint-disable-next-line
205
- return parseInt(arg.substr(1));
209
+ if (Array.isArray(arg) && /\$\d+/.test(arg.toString())) {
210
+ throw TypeError('Input can\'t be mapped to arrays (tuples/structs). Specify `mappableArgs` array in constructor.');
206
211
  }
212
+ const reference = __classPrivateFieldGet(this, _Action_instances, "m", _Action__getReturnValueReference).call(this, arg);
213
+ if (reference !== undefined)
214
+ return reference;
207
215
  // Handle SubSlots placeholder values in strategies
208
216
  if (new RegExp(/&\w+/).test(arg)) {
209
217
  if (arg === '&proxy')
@@ -34,10 +34,14 @@ export class ActionWithL2 extends Action {
34
34
  }
35
35
  numberToBytes2(number) {
36
36
  const hexNumber = number.toString(16);
37
+ if (!/^[0-9a-f]{1,4}$/i.test(hexNumber))
38
+ throw new RangeError('Value must fit in an unsigned 2-byte integer');
37
39
  return hexNumber.padStart(4, '0');
38
40
  }
39
41
  numberToBytes1(number) {
40
42
  const hexNumber = number.toString(16);
43
+ if (!/^[0-9a-f]{1,2}$/i.test(hexNumber))
44
+ throw new RangeError('Value must fit in an unsigned 1-byte integer');
41
45
  return hexNumber.padStart(2, '0');
42
46
  }
43
47
  numberToBytes32(number) {
package/esm/src/Recipe.js CHANGED
@@ -146,7 +146,9 @@ export class Recipe {
146
146
  for (const pairsPerAction of assetOwnerPairs) {
147
147
  for (const pair of pairsPerAction) {
148
148
  const isNft = !pair.asset;
149
- if (!uniqueAssetOwnerPairs.find(_pair => _pair.owner === pair.owner && (isNft ? _pair.tokenId === pair.tokenId : _pair.asset === pair.asset))) {
149
+ if (!uniqueAssetOwnerPairs.find(_pair => _pair.owner === pair.owner && (isNft
150
+ ? 'nft' in _pair && _pair.nft === pair.nft && _pair.tokenId === pair.tokenId
151
+ : _pair.asset === pair.asset))) {
150
152
  if (isNft) {
151
153
  uniqueAssetOwnerPairs.push({
152
154
  owner: pair.owner,
@@ -196,6 +198,8 @@ export class Recipe {
196
198
  }
197
199
  }
198
200
  _Recipe_instances = new WeakSet(), _Recipe__encodeForCall = function _Recipe__encodeForCall() {
201
+ if (this.actions.length === 0)
202
+ throw new Error('Cannot encode an empty recipe');
199
203
  const encoded = this.actions.map(action => action.encodeForRecipe());
200
204
  const transposed = encoded[0].map((_, colIndex) => encoded.map(row => row[colIndex]));
201
205
  const taskStruct = [
@@ -13,8 +13,8 @@ export const actionAddresses = {
13
13
  TokenBalance: '0xa92B177950F1460119940436515FD857C24494BC',
14
14
  AutomationV2Unsub: '0xe35Fb12fE9796847751076aCf5ee7d124108612C',
15
15
  SendTokenAndUnwrap: '0x4155537e6933E59a62deb68f6ad93d237d07242a',
16
- ToggleSub: '0x2120ad7b4eE1795C8B027c1592eCB918faAb163A',
17
- UpdateSub: '0xF6Cb8f7e61a64075ec8FAC3f298745605E543233',
16
+ ToggleSub: '0xf247a8525E7486884E7227EB9a161507F0FF46A6',
17
+ UpdateSub: '0x919c472bFA4B50FcACF9849501344aFDde2E4675',
18
18
  TransferNFT: '0x861e893E1796F81248e75F06C0b09Abdc8fe2f6F',
19
19
  CreateSub: '0xEB13C96257667cF0A037305A46092bf7C70A61e1',
20
20
  ApproveToken: '0xA4161cED7A29F0a3424e464a4a2dBf75888c5BF9',
@@ -356,8 +356,8 @@ export const actionAddresses = {
356
356
  SubInputs: '0x60d00020f12dd202eAfDba84e8F69E165a84c64D',
357
357
  ApproveToken: '0x842a81d2cfe26d401CD63Cc14e9DEf275c197C1a',
358
358
  SendTokenAndUnwrap: '0xd4f69250cb4d1f083Dd372FEace391A16ABbfBDc',
359
- ToggleSub: '0xfc19a12e35bb0b5e6a3d3017be9e9022a6486eee',
360
- UpdateSub: '0xAe6ea1d13dF2bE60dC7933DB56067Cb89d6A2cDe',
359
+ ToggleSub: '0xA4Eaad8Fe80d1E47f2b95CED2bcFb648d5929735',
360
+ UpdateSub: '0xe2c7D66b40e00Dd14844Db8d02142f23D4Da7071',
361
361
  CreateSub: '0x2daED8030BFC87B3d27c02E2f044B9CF4841Ff3e',
362
362
  TokenBalance: '0xC6FF5b01f7c7b35b6e093fF70D2332B361C5Be5A',
363
363
  TokenizedVaultAdapter: '0xdf31669FEd440f5BfF658ca0bBF0D22B8abdeb73',
@@ -418,14 +418,14 @@ export const actionAddresses = {
418
418
  SubInputs: '0x8ec14f9568e1d7cf73b68df899808abd7fe6fc8d',
419
419
  SendTokenAndUnwrap: '0x009C5B1c7C844Bd662Da3991295b1B3Bd71a430c',
420
420
  ApproveToken: '0x01058999F5Ac434EF48DFB5Cd942c57A7661787B',
421
- ToggleSub: '0x2eabbab99759c2ab6e9e309d42ee07d3bc7c38fe',
421
+ ToggleSub: '0x75469d8f96368F87718162e528104B59550CD71E',
422
422
  CreateSub: '0xbFD17a0a844b75976585a382a5BcCaFC9EAd731f',
423
423
  TokenBalance: '0x483B903E702F60698Dd8124558C6199922737f1F',
424
424
  TokenizedVaultAdapter: '0xD05C512bDFf6D3eAc5328807B3bC075F35271167',
425
425
  ChangeProxyOwner: '0xacf5bAD9543A98EcAcC620cF6CB989Ec88f1650D',
426
426
  PermitToken: '0x2E5E7a691092C158bb8341b936eF24FCcA6c0e00',
427
427
  HandleAuth: '0xbfcab1339e1a56a0a836cbca6457eaf71f148fcb',
428
- UpdateSub: '0x3511f444362F8cBd185DbF21C9488D16c1306461',
428
+ UpdateSub: '0x404c3EE602fb4b2D3E36E371bEA36a27B2177107',
429
429
  TransferNFT: '0xf73b14cf4bc10509446fc4111407a721f8a81f7d',
430
430
  MerklClaim: '0xdab7b1865efa8d1f8fe1602dea9285b4a599c45c',
431
431
  // aave v3
@@ -522,9 +522,9 @@ export const actionAddresses = {
522
522
  ChangeProxyOwner: '0x7b55d25BfF49Fd62563823570f47E9a35AA39541',
523
523
  PermitToken: '0x57c8ae94a5A11dA33e0518054102488b604628D0',
524
524
  HandleAuth: '0x18a90e6db79199ace00140631ef931e0bd97837c',
525
- ToggleSub: '0x5F16C0a08d52b67fc73706c494F7535Dd3382b58',
525
+ ToggleSub: '0x8879fF7E7B08cdE2e634F7Cd90219e66FB64F01D',
526
526
  CreateSub: '0xeE739937085A716477BCB5b01b0f74e1BE046645',
527
- UpdateSub: '0x1601c6ABCDE6e6d8Ad96628AFe20d47908127Aea',
527
+ UpdateSub: '0xB3598798599d3A22EA667e5557C53c139653c673',
528
528
  MerklClaim: '0xa2c6cd875c52bf5c27516fae3b6ba9241790908a',
529
529
  TransferNFT: '0xe49c70c673c3ef9019fe9c875ed5ed3dc02f5613',
530
530
  GasFeeTakerL2: '0xAEe02caf404332c40Fd7FF8d5c25F91f7c1641d0',
@@ -681,7 +681,7 @@ export const actionAddresses = {
681
681
  };
682
682
  export const otherAddresses = {
683
683
  [NETWORKS.ethereum.chainId]: {
684
- RecipeExecutor: '0xbc3Fc959FeF3F12a41738f406c02198cdeE7481F',
684
+ RecipeExecutor: '0x6e7dEde13E011Eee74E0c731A06c158377fc1Cae',
685
685
  DFSRegistry: '0x287778F121F134C66212FB16c9b53eC991D32f5b',
686
686
  DFSProxyRegistry: '0x0cCB1F91beb8590949880D2a61C26C11d6280319',
687
687
  ProxyRegistry: '0x4678f0a6958e4D2Bc4F1BAF7Bc52E8F3564f3fE4',
@@ -701,7 +701,7 @@ export const otherAddresses = {
701
701
  Empty: '0x0000000000000000000000000000000000000000',
702
702
  },
703
703
  [NETWORKS.optimism.chainId]: {
704
- RecipeExecutor: '0xEC891E6b5A93F5f29241De6B234a4e77A5456A1C',
704
+ RecipeExecutor: '0x3296C0f3Ff67B00306e5Ce251CDfA1E0160F5742',
705
705
  DFSRegistry: '0xAf707Ee480204Ed6e2640B53cE86F680D28Afcbd',
706
706
  ProxyRegistry: '0x283Cc5C26e53D66ed2Ea252D986F094B37E6e895',
707
707
  SFProxyEntryPoint: '0x06299D4A07E8C6D7C1aEc14Ab2F46DF05Dd9588E',
@@ -712,7 +712,7 @@ export const otherAddresses = {
712
712
  UniswapV3PositionManager: '0xC36442b4a4522E871399CD717aBDD847Ab11FE88',
713
713
  },
714
714
  [NETWORKS.arbitrum.chainId]: {
715
- RecipeExecutor: '0x667609f05DdC5E9Fb939eC376F07953403745cf3',
715
+ RecipeExecutor: '0x1674Df46e92A393d6B29139BF8C2d2885F40F7B2',
716
716
  DFSRegistry: '0xBF1CaC12DB60819Bfa71A328282ecbc1D40443aA',
717
717
  ProxyRegistry: '0x283Cc5C26e53D66ed2Ea252D986F094B37E6e895',
718
718
  SFProxyEntryPoint: '0x15D776C062bF292f8F70A81533E49adC7C06Cb69',
@@ -723,7 +723,7 @@ export const otherAddresses = {
723
723
  UniswapV3PositionManager: '0xC36442b4a4522E871399CD717aBDD847Ab11FE88',
724
724
  },
725
725
  [NETWORKS.base.chainId]: {
726
- RecipeExecutor: '0xc91305DdE651c899EF8eE1D0C33E7dab1B5ABF0D',
726
+ RecipeExecutor: '0xB81865b724eF6DAc41DFf125E5445701924ea19D',
727
727
  DFSRegistry: '0x347FB634271F666353F23A3362f3935D96F97476',
728
728
  ProxyRegistry: '0x425fA97285965E01Cc5F951B62A51F6CDEA5cc0d',
729
729
  SFProxyEntryPoint: '0xab6e5cde983fF98Cdb0F61f5F99cb58D40D0c837',
package/package.json CHANGED
@@ -1,19 +1,18 @@
1
1
  {
2
2
  "name": "@defisaver/sdk",
3
- "version": "1.3.36",
3
+ "version": "1.3.37",
4
4
  "description": "",
5
5
  "main": "./umd/index.js",
6
6
  "module": "./esm/src/index.js",
7
7
  "types": "./esm/src/index.d.ts",
8
8
  "scripts": {
9
9
  "test": "mocha 'test/**'",
10
+ "test:unit": "mocha 'test/**/*.js' --exclude test/DfsWeb3.js",
10
11
  "test-single": "mocha ./test/$npm_config_name.js",
11
12
  "version-bump": "git commit -am \"Version bump to $(npm version patch | cut -c 2-)\"",
12
13
  "build:esm": "rm -rf esm && tsc -p tsconfig.esm.json",
13
14
  "build:umd": "rm -rf umd && webpack --config=webpack.umd.js",
14
15
  "build": "npm run lint && npm run build:umd && npm run build:esm",
15
- "documentMD": "npx typedoc --plugin typedoc-plugin-markdown --out docs/md",
16
- "document": "npx typedoc --out docs/default",
17
16
  "lint": "eslint . --ext .ts --fix",
18
17
  "lint-check": "eslint .",
19
18
  "build-test": "npm run build && npm run test"
@@ -74,11 +73,8 @@
74
73
  "chai": "^4.2.0",
75
74
  "dotenv": "^10.0.0",
76
75
  "eslint": "^8.57.0",
77
- "jsdoc-to-markdown": "^9.1.3",
78
76
  "mocha": "^11.8.0",
79
77
  "ts-node": "^10.9.1",
80
- "typedoc": "^0.23.20",
81
- "typedoc-plugin-markdown": "^3.13.6",
82
78
  "typescript": "^4.8.4",
83
79
  "webpack": "^5.91.0",
84
80
  "webpack-cli": "^4.10.0"
package/src/Action.ts CHANGED
@@ -49,16 +49,25 @@ export class Action {
49
49
  return keccak256(this.name).substr(0, 10);
50
50
  }
51
51
 
52
+ #_getReturnValueReference(arg: unknown): number | undefined {
53
+ if (typeof arg !== 'string' || !/\$\d+/.test(arg)) return undefined;
54
+ const index = Number(arg.slice(1));
55
+ if (!/^\$\d+$/.test(arg) || !Number.isSafeInteger(index) || index < 1) {
56
+ throw new Error(`Invalid action return reference: ${arg}`);
57
+ }
58
+ return index;
59
+ }
60
+
52
61
  /**
53
62
  *
54
63
  */
55
64
  #_getArgumentMappingWithSlots(subSlots: Args) : Array<number> {
56
65
  return this.mappableArgs.map(arg => {
57
- if (new RegExp(/\$\d+/).test(arg)) {
58
- if (Array.isArray(arg)) throw TypeError('Input can\'t be mapped to arrays (tuples/structs). Specify `mappableArgs` array in constructor.');
59
- // eslint-disable-next-line
60
- return parseInt(arg.substr(1));
66
+ if (Array.isArray(arg) && /\$\d+/.test(arg.toString())) {
67
+ throw TypeError('Input can\'t be mapped to arrays (tuples/structs). Specify `mappableArgs` array in constructor.');
61
68
  }
69
+ const reference = this.#_getReturnValueReference(arg);
70
+ if (reference !== undefined) return reference;
62
71
 
63
72
  // Handle SubSlots placeholder values in strategies
64
73
  if (new RegExp(/&\w+/).test(arg)) {
@@ -77,11 +86,11 @@ export class Action {
77
86
  */
78
87
  _getArgumentMapping() : Args {
79
88
  return this.mappableArgs.map(arg => {
80
- if (new RegExp(/\$\d+/).test(arg)) {
81
- if (Array.isArray(arg)) throw TypeError('Input can\'t be mapped to arrays (tuples/structs). Specify `mappableArgs` array in constructor.');
82
- // eslint-disable-next-line
83
- return parseInt(arg.substr(1));
89
+ if (Array.isArray(arg) && /\$\d+/.test(arg.toString())) {
90
+ throw TypeError('Input can\'t be mapped to arrays (tuples/structs). Specify `mappableArgs` array in constructor.');
84
91
  }
92
+ const reference = this.#_getReturnValueReference(arg);
93
+ if (reference !== undefined) return reference;
85
94
  return 0;
86
95
  });
87
96
  }
@@ -136,7 +145,7 @@ export class Action {
136
145
  const paramTypeParsed = this._parseParamType(paramType, arg);
137
146
  if (Array.isArray(arg)) return arg.map((_arg, i) => this._replaceWithPlaceholders(_arg, paramTypeParsed[i]));
138
147
  if (typeof (paramType) === 'string') {
139
- if (new RegExp(/\$\d+/).test(arg)) return this.#_getPlaceholderForType(paramType);
148
+ if (this.#_getReturnValueReference(arg) !== undefined) return this.#_getPlaceholderForType(paramType);
140
149
  if (new RegExp(/&\w+/).test(arg)) return this.#_getPlaceholderForType(paramType);
141
150
  }
142
151
  return arg;
@@ -31,11 +31,13 @@ export class ActionWithL2 extends Action {
31
31
 
32
32
  numberToBytes2(number:number) {
33
33
  const hexNumber = number.toString(16);
34
+ if (!/^[0-9a-f]{1,4}$/i.test(hexNumber)) throw new RangeError('Value must fit in an unsigned 2-byte integer');
34
35
  return hexNumber.padStart(4, '0');
35
36
  }
36
37
 
37
38
  numberToBytes1(number:number) {
38
39
  const hexNumber = number.toString(16);
40
+ if (!/^[0-9a-f]{1,2}$/i.test(hexNumber)) throw new RangeError('Value must fit in an unsigned 1-byte integer');
39
41
  return hexNumber.padStart(2, '0');
40
42
  }
41
43
 
package/src/Recipe.ts CHANGED
@@ -57,6 +57,7 @@ export class Recipe {
57
57
  *
58
58
  */
59
59
  #_encodeForCall(): Array<string | Array<any>> {
60
+ if (this.actions.length === 0) throw new Error('Cannot encode an empty recipe');
60
61
  const encoded = this.actions.map(action => action.encodeForRecipe());
61
62
  const transposed = encoded[0].map((_, colIndex) => encoded.map(row => row[colIndex]));
62
63
  const taskStruct = [
@@ -170,7 +171,9 @@ export class Recipe {
170
171
  for (const pairsPerAction of assetOwnerPairs) {
171
172
  for (const pair of pairsPerAction) {
172
173
  const isNft = !pair.asset;
173
- if (!uniqueAssetOwnerPairs.find(_pair => _pair.owner === pair.owner && (isNft ? _pair.tokenId === pair.tokenId : _pair.asset === pair.asset))) {
174
+ if (!uniqueAssetOwnerPairs.find(_pair => _pair.owner === pair.owner && (isNft
175
+ ? 'nft' in _pair && _pair.nft === pair.nft && _pair.tokenId === pair.tokenId
176
+ : _pair.asset === pair.asset))) {
174
177
  if (isNft) {
175
178
  uniqueAssetOwnerPairs.push({
176
179
  owner: pair.owner!,
package/src/addresses.ts CHANGED
@@ -15,8 +15,8 @@ export const actionAddresses = {
15
15
  TokenBalance: '0xa92B177950F1460119940436515FD857C24494BC',
16
16
  AutomationV2Unsub: '0xe35Fb12fE9796847751076aCf5ee7d124108612C',
17
17
  SendTokenAndUnwrap: '0x4155537e6933E59a62deb68f6ad93d237d07242a',
18
- ToggleSub: '0x2120ad7b4eE1795C8B027c1592eCB918faAb163A',
19
- UpdateSub: '0xF6Cb8f7e61a64075ec8FAC3f298745605E543233',
18
+ ToggleSub: '0xf247a8525E7486884E7227EB9a161507F0FF46A6',
19
+ UpdateSub: '0x919c472bFA4B50FcACF9849501344aFDde2E4675',
20
20
  TransferNFT: '0x861e893E1796F81248e75F06C0b09Abdc8fe2f6F',
21
21
  CreateSub: '0xEB13C96257667cF0A037305A46092bf7C70A61e1',
22
22
  ApproveToken: '0xA4161cED7A29F0a3424e464a4a2dBf75888c5BF9',
@@ -430,8 +430,8 @@ export const actionAddresses = {
430
430
  SubInputs: '0x60d00020f12dd202eAfDba84e8F69E165a84c64D',
431
431
  ApproveToken: '0x842a81d2cfe26d401CD63Cc14e9DEf275c197C1a',
432
432
  SendTokenAndUnwrap: '0xd4f69250cb4d1f083Dd372FEace391A16ABbfBDc',
433
- ToggleSub: '0xfc19a12e35bb0b5e6a3d3017be9e9022a6486eee',
434
- UpdateSub: '0xAe6ea1d13dF2bE60dC7933DB56067Cb89d6A2cDe',
433
+ ToggleSub: '0xA4Eaad8Fe80d1E47f2b95CED2bcFb648d5929735',
434
+ UpdateSub: '0xe2c7D66b40e00Dd14844Db8d02142f23D4Da7071',
435
435
  CreateSub: '0x2daED8030BFC87B3d27c02E2f044B9CF4841Ff3e',
436
436
  TokenBalance: '0xC6FF5b01f7c7b35b6e093fF70D2332B361C5Be5A',
437
437
  TokenizedVaultAdapter: '0xdf31669FEd440f5BfF658ca0bBF0D22B8abdeb73',
@@ -500,14 +500,14 @@ export const actionAddresses = {
500
500
  SubInputs: '0x8ec14f9568e1d7cf73b68df899808abd7fe6fc8d',
501
501
  SendTokenAndUnwrap: '0x009C5B1c7C844Bd662Da3991295b1B3Bd71a430c',
502
502
  ApproveToken: '0x01058999F5Ac434EF48DFB5Cd942c57A7661787B',
503
- ToggleSub: '0x2eabbab99759c2ab6e9e309d42ee07d3bc7c38fe',
503
+ ToggleSub: '0x75469d8f96368F87718162e528104B59550CD71E',
504
504
  CreateSub: '0xbFD17a0a844b75976585a382a5BcCaFC9EAd731f',
505
505
  TokenBalance: '0x483B903E702F60698Dd8124558C6199922737f1F',
506
506
  TokenizedVaultAdapter: '0xD05C512bDFf6D3eAc5328807B3bC075F35271167',
507
507
  ChangeProxyOwner: '0xacf5bAD9543A98EcAcC620cF6CB989Ec88f1650D',
508
508
  PermitToken: '0x2E5E7a691092C158bb8341b936eF24FCcA6c0e00',
509
509
  HandleAuth: '0xbfcab1339e1a56a0a836cbca6457eaf71f148fcb',
510
- UpdateSub: '0x3511f444362F8cBd185DbF21C9488D16c1306461',
510
+ UpdateSub: '0x404c3EE602fb4b2D3E36E371bEA36a27B2177107',
511
511
  TransferNFT: '0xf73b14cf4bc10509446fc4111407a721f8a81f7d',
512
512
  MerklClaim: '0xdab7b1865efa8d1f8fe1602dea9285b4a599c45c',
513
513
 
@@ -619,9 +619,9 @@ export const actionAddresses = {
619
619
  ChangeProxyOwner: '0x7b55d25BfF49Fd62563823570f47E9a35AA39541',
620
620
  PermitToken: '0x57c8ae94a5A11dA33e0518054102488b604628D0',
621
621
  HandleAuth: '0x18a90e6db79199ace00140631ef931e0bd97837c',
622
- ToggleSub: '0x5F16C0a08d52b67fc73706c494F7535Dd3382b58',
622
+ ToggleSub: '0x8879fF7E7B08cdE2e634F7Cd90219e66FB64F01D',
623
623
  CreateSub: '0xeE739937085A716477BCB5b01b0f74e1BE046645',
624
- UpdateSub: '0x1601c6ABCDE6e6d8Ad96628AFe20d47908127Aea',
624
+ UpdateSub: '0xB3598798599d3A22EA667e5557C53c139653c673',
625
625
  MerklClaim: '0xa2c6cd875c52bf5c27516fae3b6ba9241790908a',
626
626
  TransferNFT: '0xe49c70c673c3ef9019fe9c875ed5ed3dc02f5613',
627
627
 
@@ -798,7 +798,7 @@ export const actionAddresses = {
798
798
 
799
799
  export const otherAddresses = {
800
800
  [NETWORKS.ethereum.chainId]: {
801
- RecipeExecutor: '0xbc3Fc959FeF3F12a41738f406c02198cdeE7481F',
801
+ RecipeExecutor: '0x6e7dEde13E011Eee74E0c731A06c158377fc1Cae',
802
802
  DFSRegistry: '0x287778F121F134C66212FB16c9b53eC991D32f5b',
803
803
  DFSProxyRegistry: '0x0cCB1F91beb8590949880D2a61C26C11d6280319',
804
804
  ProxyRegistry: '0x4678f0a6958e4D2Bc4F1BAF7Bc52E8F3564f3fE4',
@@ -820,7 +820,7 @@ export const otherAddresses = {
820
820
  Empty: '0x0000000000000000000000000000000000000000',
821
821
  },
822
822
  [NETWORKS.optimism.chainId]: {
823
- RecipeExecutor: '0xEC891E6b5A93F5f29241De6B234a4e77A5456A1C',
823
+ RecipeExecutor: '0x3296C0f3Ff67B00306e5Ce251CDfA1E0160F5742',
824
824
  DFSRegistry: '0xAf707Ee480204Ed6e2640B53cE86F680D28Afcbd',
825
825
  ProxyRegistry: '0x283Cc5C26e53D66ed2Ea252D986F094B37E6e895',
826
826
  SFProxyEntryPoint: '0x06299D4A07E8C6D7C1aEc14Ab2F46DF05Dd9588E',
@@ -833,7 +833,7 @@ export const otherAddresses = {
833
833
  UniswapV3PositionManager: '0xC36442b4a4522E871399CD717aBDD847Ab11FE88',
834
834
  },
835
835
  [NETWORKS.arbitrum.chainId]: {
836
- RecipeExecutor: '0x667609f05DdC5E9Fb939eC376F07953403745cf3',
836
+ RecipeExecutor: '0x1674Df46e92A393d6B29139BF8C2d2885F40F7B2',
837
837
  DFSRegistry: '0xBF1CaC12DB60819Bfa71A328282ecbc1D40443aA',
838
838
  ProxyRegistry: '0x283Cc5C26e53D66ed2Ea252D986F094B37E6e895',
839
839
  SFProxyEntryPoint: '0x15D776C062bF292f8F70A81533E49adC7C06Cb69',
@@ -846,7 +846,7 @@ export const otherAddresses = {
846
846
  UniswapV3PositionManager: '0xC36442b4a4522E871399CD717aBDD847Ab11FE88',
847
847
  },
848
848
  [NETWORKS.base.chainId]: {
849
- RecipeExecutor: '0xc91305DdE651c899EF8eE1D0C33E7dab1B5ABF0D',
849
+ RecipeExecutor: '0xB81865b724eF6DAc41DFf125E5445701924ea19D',
850
850
  DFSRegistry: '0x347FB634271F666353F23A3362f3935D96F97476',
851
851
  ProxyRegistry: '0x425fA97285965E01Cc5F951B62A51F6CDEA5cc0d',
852
852
  SFProxyEntryPoint: '0xab6e5cde983fF98Cdb0F61f5F99cb58D40D0c837',
package/test/Action.js CHANGED
@@ -39,7 +39,8 @@ describe('Action', () => {
39
39
  let action;
40
40
  it('Constructor', () => {
41
41
  action = new dfs.Action('MockSwap', '0x0a80C3C540eEF99811f4579fa7b1A0617294e06f', ['uint256', 'address'], ['$1', '0x6b175474e89094c44da98b954eedeac495271d0f']);
42
- action.mappableArgs = [action.args[0][0], action.args[0][1]];
42
+ action.mappableArgs = [action.args[0], action.args[1]];
43
+ assert.deepEqual(action.encodeForRecipe()[3], [1, 0]);
43
44
  })
44
45
  it('encodeForDsProxyCall', () => encodeForDsProxyCall(action));
45
46
  it('encodeForRecipe', () => encodeForRecipe(action));
@@ -55,8 +56,87 @@ describe('Action', () => {
55
56
  assert.throws(() => encodeForRecipe(action));
56
57
  });
57
58
  it('encodeForRecipe with custom mappableArgs', () => {
58
- action.mappableArgs = [action.args[0][0][0], action.args[0][1]];
59
- encodeForRecipe(action)
59
+ action.mappableArgs = [action.args[0][0], action.args[1]];
60
+ assert.deepEqual(action.encodeForRecipe()[3], [1, 0]);
60
61
  });
61
62
  })
62
63
  })
64
+
65
+
66
+ describe('Action encoding', () => {
67
+ const AbiCoder = require('web3-eth-abi');
68
+ const target = '0x0000000000000000000000000000000000000001';
69
+ const recipient = '0x0000000000000000000000000000000000000002';
70
+ const zeroWord = '0'.repeat(64);
71
+
72
+ it('encodes the target, selector, arguments, ID and unmapped inputs', () => {
73
+ const action = new dfs.Action('MockSwap', target, ['uint256', 'address'], ['5', recipient]);
74
+ // Static ABI tuple: one word for the amount, one for the address.
75
+ const payload = `0x${'5'.padStart(64, '0')}${'2'.padStart(64, '0')}`;
76
+ assert.deepEqual(action.encodeForRecipe(), [payload, `0x${zeroWord}`, '0xe45f86c0', [0, 0]]);
77
+ const [address, data] = action.encodeForDsProxyCall();
78
+ assert.equal(address, target);
79
+ assert.equal(data.slice(0, 10), '0x389f87ff'); // executeActionDirect(bytes)
80
+ assert.equal(AbiCoder.decodeParameter('bytes', `0x${data.slice(10)}`), payload);
81
+ });
82
+
83
+ it('replaces mapped inputs with zeros without changing the original arguments', () => {
84
+ const args = ['$2', recipient];
85
+ const action = new dfs.Action('MockSwap', target, ['uint256', 'address'], args);
86
+ const [payload, , , mapping] = action.encodeForRecipe();
87
+ assert.equal(payload, `0x${zeroWord}${'2'.padStart(64, '0')}`);
88
+ assert.deepEqual(mapping, [2, 0]);
89
+ assert.deepEqual(args, ['$2', recipient]);
90
+ });
91
+
92
+ it('preserves nested tuple and array values around mapped inputs', () => {
93
+ const args = [['7', ['$1', recipient]], ['11', '13']];
94
+ const action = new dfs.Action('MockSwap', target,
95
+ [['uint256', ['uint256', 'address']], 'uint256[]'], args);
96
+ action.mappableArgs = [args[0][1][0], args[0][1][1]];
97
+ const [payload, , , mapping] = action.encodeForRecipe();
98
+ const decoded = AbiCoder.decodeParameter('((uint256,(uint256,address)),uint256[])', payload);
99
+ assert.equal(decoded[0][0], '7');
100
+ assert.equal(decoded[0][1][0], '0');
101
+ assert.equal(decoded[0][1][1], recipient);
102
+ assert.deepEqual(decoded[1], ['11', '13']);
103
+ assert.deepEqual(mapping, [1, 0]);
104
+ assert.equal(args[0][1][0], '$1');
105
+ });
106
+
107
+ it('maps strategy slots and special wallet references', () => {
108
+ const action = new dfs.Action('MockSwap', target,
109
+ ['uint256', 'uint256', 'address', 'address', 'uint256'],
110
+ ['$1', '&amount', '&proxy', '&eoa', '9']);
111
+ assert.deepEqual(action.encodeForStrategy({ '&amount': { index: 128 } }),
112
+ ['0xe45f86c0', [1, 128, 254, 255, 0]]);
113
+ });
114
+
115
+ it('rejects mismatched argument counts', () => {
116
+ assert.throws(() => new dfs.Action('MockSwap', target, ['uint256'], []),
117
+ 'Parameters/arguments length mismatch');
118
+ });
119
+ });
120
+
121
+
122
+ describe('Action reference validation', () => {
123
+ const target = '0x0000000000000000000000000000000000000001';
124
+
125
+ for (const reference of ['$0', '$1junk', 'prefix$1', '$1.5', '$9007199254740992']) {
126
+ it(`rejects malformed reference ${reference} in every encoding path`, () => {
127
+ const action = new dfs.Action('MockSwap', target, ['uint256'], [reference]);
128
+ for (const encode of [
129
+ () => action.encodeForDsProxyCall(),
130
+ () => action.encodeForRecipe(),
131
+ () => action.encodeForStrategy({}),
132
+ ]) {
133
+ assert.throws(encode, `Invalid action return reference: ${reference}`);
134
+ }
135
+ });
136
+ }
137
+
138
+ it('preserves existing leading-zero references and ordinary strings', () => {
139
+ const action = new dfs.Action('MockSwap', target, ['uint256', 'string'], ['$01', '$price']);
140
+ assert.deepEqual(action.encodeForRecipe()[3], [1, 0]);
141
+ });
142
+ });