@defisaver/sdk 0.1.7 → 0.1.11
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/ACTIONS.md +236 -19
- package/AccessLists/AaveAccessLists.js +28 -0
- package/AccessLists/BalancerAccessLists.js +20 -0
- package/AccessLists/CompoundAccessLists.js +33 -0
- package/AccessLists/DyDxAccessLists.js +14 -0
- package/AccessLists/FlashLoanAccessLists.js +27 -0
- package/AccessLists/GuniAccessLists.js +15 -0
- package/AccessLists/InstaAccessLists.js +17 -0
- package/AccessLists/LidoAccessLists.js +21 -0
- package/AccessLists/LiquityAccessLists.js +81 -0
- package/AccessLists/MStableAccessLists.js +18 -0
- package/AccessLists/MakerAccessLists.js +44 -0
- package/AccessLists/RariAccessLists.js +12 -0
- package/AccessLists/ReflexerAccessLists.js +53 -0
- package/AccessLists/UniswapAccessLists.js +41 -0
- package/AccessLists/UtilsAccessLists.js +41 -0
- package/AccessLists/YearnAccessLists.js +14 -0
- package/AccessLists/index.js +41 -0
- package/DEV.md +14 -0
- package/package.json +2 -2
- package/src/Action.js +13 -0
- package/src/Recipe.js +21 -1
- package/src/actions/aave/index.js +0 -2
- package/src/actions/basic/AutomationV2Unsub.js +2 -2
- package/src/actions/flashloan/BalancerFlashLoanAction.js +2 -2
- package/src/actions/flashloan/BalancerFlashLoanPaybackAction.js +2 -2
- package/src/actions/flashloan/MakerFlashLoanPaybackAction.js +2 -2
- package/src/actions/guni/GUniDeposit.js +43 -0
- package/src/actions/guni/GUniWithdraw.js +32 -0
- package/src/actions/guni/index.js +6 -0
- package/src/actions/index.js +6 -0
- package/src/actions/mstable/MStableClaimAction.js +38 -0
- package/src/actions/mstable/MStableDepositAction.js +63 -0
- package/src/actions/mstable/MStableWithdrawAction.js +66 -0
- package/src/actions/mstable/index.js +9 -0
- package/src/actions/rari/RariDepositAction.js +31 -0
- package/src/actions/rari/RariWithdrawAction.js +33 -0
- package/src/actions/rari/index.js +6 -0
- package/src/addresses.js +16 -2
- package/test/accessLists/MockAccessLists.json +1482 -0
- package/test/accessLists/Recipe.js +123 -0
- package/test/accessLists/access-lists.js +101 -0
- package/src/actions/aave/AaveMigrateLendAction.js +0 -18
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
const AbiCoder = require('web3-eth-abi');
|
|
2
|
+
const { BN } = require('web3-utils');
|
|
3
|
+
const {getAssetInfo, utils: {compare}} = require("@defisaver/tokens");
|
|
4
|
+
const Action = require('../../src/Action');
|
|
5
|
+
const {getAddr} = require('../../src/addresses');
|
|
6
|
+
const RecipeAbi = require('../../src/abis/Recipe.json');
|
|
7
|
+
const MockAccessLists = require('./MockAccessLists');
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Set of Actions to be performed sequentially in a single transaction
|
|
11
|
+
*/
|
|
12
|
+
class Recipe {
|
|
13
|
+
/**
|
|
14
|
+
* @param name {string}
|
|
15
|
+
* @param actions {Array<Action>}
|
|
16
|
+
*/
|
|
17
|
+
constructor(name, actions = []) {
|
|
18
|
+
actions.forEach((action) => {
|
|
19
|
+
if (!action instanceof Action) throw new TypeError('Supplied action does not inherit Action');
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
this.name = name;
|
|
23
|
+
this.actions = actions;
|
|
24
|
+
this.taskExecutorAddress = getAddr('TaskExecutor');
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @param action {Action}
|
|
29
|
+
* @returns {Recipe}
|
|
30
|
+
*/
|
|
31
|
+
addAction(action) {
|
|
32
|
+
if (!action instanceof Action) throw new TypeError('Supplied action does not inherit Action');
|
|
33
|
+
this.actions.push(action);
|
|
34
|
+
return this;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Encode arguments for calling the action set directly
|
|
39
|
+
* @returns {Array<string|Array<*>>}
|
|
40
|
+
* @private
|
|
41
|
+
*/
|
|
42
|
+
_encodeForCall() {
|
|
43
|
+
const encoded = this.actions.map(action => action.encodeForRecipe());
|
|
44
|
+
const transposed = encoded[0].map((_, colIndex) => encoded.map(row => row[colIndex]));
|
|
45
|
+
const taskStruct = [
|
|
46
|
+
this.name,
|
|
47
|
+
...transposed,
|
|
48
|
+
];
|
|
49
|
+
return [taskStruct];
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Encode arguments for calling the action set via DsProxy
|
|
54
|
+
* @returns {Array<string>} `address` & `data` to be passed on to DSProxy's `execute(address _target, bytes memory _data)`
|
|
55
|
+
*/
|
|
56
|
+
encodeForDsProxyCall() {
|
|
57
|
+
const executeTaskAbi = RecipeAbi.find(({name}) => name === 'executeTask');
|
|
58
|
+
return [
|
|
59
|
+
this.taskExecutorAddress,
|
|
60
|
+
AbiCoder.encodeFunctionCall(executeTaskAbi, this._encodeForCall()),
|
|
61
|
+
];
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Logs parameter mapping in verbose format for validation. Used for testing in development.
|
|
66
|
+
*/
|
|
67
|
+
_validateParamMappings() {
|
|
68
|
+
this.actions.forEach((action, i) => {
|
|
69
|
+
action.getArgumentMapping().forEach((source, j) => {
|
|
70
|
+
if (source) console.log(`${this.actions[i].name} takes argument #${j + 1} from ${this.actions[source - 1].name} (action #${source})`);
|
|
71
|
+
})
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Assets requiring approval to be used by DsProxy
|
|
77
|
+
* Approval is done from owner to DsProxy
|
|
78
|
+
* @returns {Promise<Array<{owner: string, asset: string}>>}
|
|
79
|
+
*/
|
|
80
|
+
async getAssetsToApprove() {
|
|
81
|
+
const uniqueAssetOwnerPairs = [];
|
|
82
|
+
const assetOwnerPairs = await Promise.all(this.actions.map(a => a.getAssetsToApprove()));
|
|
83
|
+
for (const pairsPerAction of assetOwnerPairs) {
|
|
84
|
+
for (const pair of pairsPerAction) {
|
|
85
|
+
const isNft = !pair.asset;
|
|
86
|
+
if (!uniqueAssetOwnerPairs.find(_pair => _pair.owner === pair.owner && (isNft ? _pair.tokenId === pair.tokenId : _pair.asset === pair.asset))) {
|
|
87
|
+
uniqueAssetOwnerPairs.push(pair);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return uniqueAssetOwnerPairs.filter(({ address }) => !compare(address, getAssetInfo('ETH').address));
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* ETH value to be sent with transaction
|
|
96
|
+
* @returns {Promise<string>} ETH value in wei
|
|
97
|
+
*/
|
|
98
|
+
async getEthValue() {
|
|
99
|
+
return (await Promise.all(this.actions.map(a => a.getEthValue())))
|
|
100
|
+
.reduce((acc, val) => acc.add(new BN(val)), new BN(0))
|
|
101
|
+
.toString();
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Generates an access list for the recipe
|
|
106
|
+
* @returns {Array<*>}
|
|
107
|
+
*/
|
|
108
|
+
getAccessList() {
|
|
109
|
+
const addressMapping = {
|
|
110
|
+
[getAddr('TaskExecutor')]: [],
|
|
111
|
+
[getAddr('DFSRegistry')]: [],
|
|
112
|
+
};
|
|
113
|
+
this.actions.forEach((action) => {
|
|
114
|
+
const accessList = MockAccessLists[action.name];
|
|
115
|
+
accessList.forEach(([address, memoryLocations]) => {
|
|
116
|
+
addressMapping[address] = new Set([...memoryLocations, ...(addressMapping[address] || [])]);
|
|
117
|
+
})
|
|
118
|
+
});
|
|
119
|
+
return Object.keys(addressMapping).map((addr) => [addr, [...addressMapping[addr]]]);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
module.exports = Recipe;
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
const { assert } = require("chai");
|
|
2
|
+
const dfs = require("../../index.js");
|
|
3
|
+
const Recipe = require("./Recipe");
|
|
4
|
+
const MockAccessLists = require('./MockAccessLists');
|
|
5
|
+
|
|
6
|
+
const testDuplicates = (accessList) => {
|
|
7
|
+
const contractAddrs = accessList.map((entry) => {
|
|
8
|
+
assert(
|
|
9
|
+
entry[1].length == [...new Set(entry[1])].length,
|
|
10
|
+
'Duplicate storage address found',
|
|
11
|
+
);
|
|
12
|
+
return entry[0];
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
assert(
|
|
16
|
+
accessList.length == [...new Set(contractAddrs)].length,
|
|
17
|
+
'Duplicate contract address found',
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
return true;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const testInclusion = (recipe, accessList) => {
|
|
24
|
+
const actions = recipe.actions;
|
|
25
|
+
const addressMapping = Object.fromEntries(accessList);
|
|
26
|
+
actions.forEach((action) => {
|
|
27
|
+
MockAccessLists[action.name].forEach((entry) => {
|
|
28
|
+
const storageAddrs = new Set(addressMapping[entry[0]]);
|
|
29
|
+
assert(
|
|
30
|
+
storageAddrs,
|
|
31
|
+
'Access list missing a contract address',
|
|
32
|
+
);
|
|
33
|
+
entry[1].forEach((addr) => {
|
|
34
|
+
assert(
|
|
35
|
+
storageAddrs.has(addr),
|
|
36
|
+
'Access list missing a storage address',
|
|
37
|
+
);
|
|
38
|
+
})
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
return true;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const deadbeef = '0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef';
|
|
46
|
+
|
|
47
|
+
const sampleRecipes = [
|
|
48
|
+
new Recipe('recMakerRepay', [
|
|
49
|
+
new dfs.actions.maker.MakerWithdrawAction(0, 0, deadbeef, deadbeef),
|
|
50
|
+
new dfs.actions.basic.SellAction(0, deadbeef, deadbeef, 0),
|
|
51
|
+
new dfs.actions.maker.MakerPaybackAction(0, '$2', deadbeef, deadbeef),
|
|
52
|
+
]),
|
|
53
|
+
new Recipe('recMakerFLBoost', [
|
|
54
|
+
new dfs.actions.flashloan.MakerFlashLoanAction(0, deadbeef, ''),
|
|
55
|
+
new dfs.actions.basic.SellAction('', deadbeef, deadbeef, 0),
|
|
56
|
+
new dfs.actions.maker.MakerSupplyAction(0, '$2', deadbeef, deadbeef),
|
|
57
|
+
new dfs.actions.maker.MakerGenerateAction(0, '$1', deadbeef),
|
|
58
|
+
]),
|
|
59
|
+
new Recipe('recMakerFRepayViaDebt', [
|
|
60
|
+
new dfs.actions.flashloan.MakerFlashLoanAction(0, deadbeef, ''),,
|
|
61
|
+
new dfs.actions.maker.MakerPaybackAction(0, 0, deadbeef, deadbeef),
|
|
62
|
+
new dfs.actions.maker.MakerWithdrawAction(0, 0, deadbeef, deadbeef, deadbeef),
|
|
63
|
+
new dfs.actions.basic.SellAction('', deadbeef, deadbeef, 0),
|
|
64
|
+
new dfs.actions.basic.SendTokenAction(deadbeef, deadbeef, '$1'),
|
|
65
|
+
]),
|
|
66
|
+
new Recipe('recCompoundFLBoostViaColl', [
|
|
67
|
+
new dfs.actions.flashloan.MakerFlashLoanAction(0, deadbeef, []),
|
|
68
|
+
new dfs.actions.compound.CompoundSupplyAction(deadbeef, '$1', deadbeef, true),
|
|
69
|
+
new dfs.actions.compound.CompoundBorrowAction(deadbeef, 0, deadbeef),
|
|
70
|
+
]),
|
|
71
|
+
new Recipe('recCompoundFLRepayViaDebt', [
|
|
72
|
+
new dfs.actions.flashloan.MakerFlashLoanAction(0, deadbeef, []),
|
|
73
|
+
new dfs.actions.compound.CompoundSupplyAction(deadbeef, '$1', deadbeef, true),
|
|
74
|
+
new dfs.actions.compound.CompoundWithdrawAction(deadbeef, 0, deadbeef),
|
|
75
|
+
]),
|
|
76
|
+
];
|
|
77
|
+
|
|
78
|
+
describe('Access-Lists', () => {
|
|
79
|
+
sampleRecipes.forEach((recipe) => {
|
|
80
|
+
it(`... should make an access list for recipe ${recipe.name}`, () => {
|
|
81
|
+
const recipeAccessList = recipe.getAccessList();
|
|
82
|
+
testDuplicates(recipeAccessList);
|
|
83
|
+
testInclusion(recipe, recipeAccessList);
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('... should handle duplicate actions in recipe correctly', () => {
|
|
88
|
+
const recipe = new Recipe('mockRecipe', [
|
|
89
|
+
new dfs.actions.flashloan.MakerFlashLoanAction(0, deadbeef, []),
|
|
90
|
+
new dfs.actions.compound.CompoundSupplyAction(deadbeef, '$1', deadbeef, true),
|
|
91
|
+
new dfs.actions.compound.CompoundSupplyAction(deadbeef, '$1', deadbeef, true),
|
|
92
|
+
new dfs.actions.compound.CompoundSupplyAction(deadbeef, '$1', deadbeef, true),
|
|
93
|
+
new dfs.actions.compound.CompoundSupplyAction(deadbeef, '$1', deadbeef, true),
|
|
94
|
+
new dfs.actions.compound.CompoundWithdrawAction(deadbeef, 0, deadbeef),
|
|
95
|
+
]);
|
|
96
|
+
|
|
97
|
+
const recipeAccessList = recipe.getAccessList();
|
|
98
|
+
testDuplicates(recipeAccessList);
|
|
99
|
+
testInclusion(recipe, recipeAccessList);
|
|
100
|
+
});
|
|
101
|
+
});
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
const Action = require("../../Action");
|
|
2
|
-
const { getAddr } = require('../../addresses.js');
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* AaveMigrateLendAction - Migrates Lend to Aave
|
|
6
|
-
*/
|
|
7
|
-
class AaveMigrateLendAction extends Action {
|
|
8
|
-
/**
|
|
9
|
-
* @param lendAmount {string}
|
|
10
|
-
* @param from {EthAddress}
|
|
11
|
-
* @param to {EthAddress}
|
|
12
|
-
*/
|
|
13
|
-
constructor(lendAmount, from, to) {
|
|
14
|
-
super('AaveMigrateLend', getAddr('AaveMigrateLend'), ["uint256", "address", "address"], [...arguments]);
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
module.exports = AaveMigrateLendAction;
|